一、计算精度现象举例
举例1、加法
举例2、减法
举例3、乘法
举例3、除法
二、JS为什么会有计算精度的问题
JavaScript 内部只有一种数字类型Number,也就是说,JavaScript 语言的底层根本没有整数,所有数字都是以IEEE-754标准格式64位浮点数形式储存,1与1.0是相同的。因为有些小数以二进制表示位数是无穷的。JavaScript会把超出53位之后的二进制舍弃,所以涉及小数的比较和运算要特别小心。
IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。
三、解决方法
项目技术栈vue3+vite+ts
3.1、方法一,同时扩大倍数再除以相同的倍数
(x * 10 ^ n + y * 10 ^ n)/ 10 ^ n
0.1 +0.2
// 0.30000000000000004
(0.1 *10 + 0.2 *10) / 10
// 0.3
3.2、方法二,toFixed保留小数位数,依然存在精度问题
3.3、方法三,mathjs - npm
pnpm add mathjs
Weekly Download 580197 (20230324)
方法 | 运算 | 使用 | 运输结果 |
add |
加法 | add(1, 2) |
3 |
subtract |
减法 | subtract(2, 1) |
1 |
multiply |
乘法 | multiply(2, 2) |
4 |
divide |
除法 | divide(4, 2) |
2 |
round |
四舍五入 | round(4.01) |
4 |
bignumber |
转换为bigNumber类型。对于具有任意精度的计算,math.js支持BigNumber数据类型,bignumber返回一个Decimal类,精度依然难以保证 | bignumber(4.01) |
|
evaluate |
直接运算表达式 |
evaluate('(4.01 + 3) / 2') |
3.505 |
sqrt |
平方根计算 |
sqrt(4) |
2 |
pow |
x 的 y 次幂值 | pow(3,3) | 27 |
chain |
链式操作 |
chain(3).add(4).multiply(2).done() |
14 |
atan2 |
返回其参数商的反正切值 | atan2(15,30) |
0.4636476090008061 |
log | 返回给定数字的自然Log值(即e的底数) | log(9) |
2.1972245773362196 |
pi |
圆周率 | console.log('pi:', pi) |
3.141592653589793 |
e | 欧拉常数和自然对数的基数,约为 2.718 | console.log('e:', e) |
2.718281828459045 |
derivative |
待考证 | console.log('derivative:',derivative('x^2 + x', 'x')) |
|
matrix |
矩阵操作 |
matrix([0, 1, 2, 3, 4]) |
3.4、方法四,bignumber.js - npm
Weekly Download 8826960 (20230324)
pnpm add bignumber.js
const num = new BigNumber(1234567890.0123456789) | |||
const num1 = new BigNumber(123.123) | |||
方法 | 运算 | 使用 | 运算结果 |
toFormat |
格式化 |
num.toFormat() |
1,234,567,890.0123458 保留了七位小数,第七位依据第八位四舍五入 |
toFormat |
格式化 |
num.toFormat(3) |
1,234,567,890.012 保留三位小数 |
toFormat |
格式化 |
num.toFormat(13) |
1,234,567,890.012 保留十三位小数,实际还是保留了七位小数,第七位依据第八位四舍五入,然后位数用0补足 |
plus |
加法 | num1.plus(1.1) |
|
minus |
减法 | num1.minus(1.1) |
|
times |
乘法 | num1.times(2) |
|
div |
除法 | num1.div(2) |
|
mod |
取余 |
num1.mod(2) |
|
x.eq(y) |
isEqualTo--是否相等 |
num.eq(num1) |
false |
x.gt(y) |
isGreaterThan--是否大于 |
num.gt(num1) |
true |
x.gte(y) |
isGreaterThanOrEqualTo--是否大于等于 |
num.gte(num1) |
true |
x.lt(y) |
isLessThan--是否小于 |
num.lt(num1) |
false |
x.lte(y) |
isLessThanOrEqualTo--是否小于等于 |
num.lte(num1) |
false |
negated |
取非,改变数字的正负号 |
num.negated() |
3.5、方法五,big.js - npm
Weekly Download 21,339,420 (20230324)
pnpm add @types/big.js
const num = new Big(1234567890.0123456789) | |||
const num1 = new Big(123.123) | |||
方法 | 运算 | 使用 | 运算结果 |
plus |
加法 | num1.plus(1.1) |
|
minus |
减法 | num1.minus(1.1) |
|
times |
乘法 | num1.times(2) |
|
div |
除法 | num1.div(2) |
|
mod |
取余 |
num1.mod(2) |
|
x.eq(y) |
isEqualTo--是否相等 |
num.eq(num1) |
false |
x.gt(y) |
isGreaterThan--是否大于 |
num.gt(num1) |
true |
x.gte(y) |
isGreaterThanOrEqualTo--是否大于等于 |
num.gte(num1) |
true |
x.lt(y) |
isLessThan--是否小于 |
num.lt(num1) |
false |
x.lte(y) |
isLessThanOrEqualTo--是否小于等于 |
num.lte(num1) |
false |
3.6、方法六,decimal.js - npm
Weekly Download 16,251,713 (20230324)
pnpm add decimal.js
const num = new Big(1234567890.0123456789) | |||
const num1 = new Big(123.123) | |||
方法 | 运算 | 使用 | 运算结果 |
plus |
加法 | num1.plus(new Decimal(1.1)) |
|
minus |
减法 | num1.minus(new Decimal(1.1)) |
|
times |
乘法 | num1.times(new Decimal(2)) |
|
div |
除法 | num1.div(new Decimal(2)) |
|
mod |
取余 |
num1.mod(new Decimal(2)) |
|
x.eq(y) |
isEqualTo--是否相等 |
num.eq(num1) |
false |
x.gt(y) |
isGreaterThan--是否大于 |
num.gt(num1) |
true |
x.gte(y) |
isGreaterThanOrEqualTo--是否大于等于 |
num.gte(num1) |
true |
x.lt(y) |
isLessThan--是否小于 |
num.lt(num1) |
false |
x.lte(y) |
isLessThanOrEqualTo--是否小于等于 |
num.lte(num1) |
false |
四、欢迎交流指正,关注我,一起学习。
参考链接:
JavaScript Math 参考手册