💡 Tips:这个问题有很多的解决方式,通过一些主流的库可以有效避免
在计算数字时,有时会遇到精度丢失的问题;顾名思义,就是计算后得到的结果不是我们想要的结果。
举个例子:
console.log(0.05 + 0.01) // 0.060000000000000005
// 本应该是 0.06,但是实际的结果是 0.060000000000000005
这是因为js小数点后的运算是会出现精度丢失的问题
js中 number类型运算都需要先将十进制转二进制。但小数点后的位数转二进制会出现无限循环的问题,只能舍0入1,所以会出现小数点丢失问题。
0.1 => 首先将 0.1 乘以 2,得到 0.2。将 0.2 的整数部分 0 写入二进制小数的第一位,然后将 0.2 的小数部分 0.2 - 0 = 0.2 继续乘以 2,得到 0.4。将 0.4 的整数部分 0 写入二进制小数的第二位,然后将 0.4 的小数部分 0.4 - 0 = 0.4 继续乘以 2,得到 0.8。将 0.8 的整数部分 0 写入二进制小数的第三位,然后将 0.8 的小数部分 0.8 - 0 = 0.8 继续乘以 2,得到 1.6。将 1.6 的整数部分 1 写入二进制小数的第四位,然后将 1.6 的小数部分 1.6 - 1 = 0.6 继续乘以 2,得到 1.2。将 1.2 的整数部分 1 写入二进制小数的第五位,然后将 1.2 的小数部分 1.2 - 1 = 0.2 继续乘以 2,得到 0.4。由于小数部分已经出现过,因此小数部分开始循环,直到达到所需的精度。
转换的结果为 0.00011001100110011001100110011001100110011001100110011010…,其中小数部分无限循环。这个值和 0.1 相差比较大,精度已经丢失了。
将 0.1 转换为科学计数法形式,即 1.0 * 10^-1,然后将 10^-1 转换为二进制小数形式,得到 0.0 0011 0011 0011 0011 0011 0011 0011…。这个值和 0.1 相差较小,但精度仍然丢失。
toFixed() 方法,参数为保留几位小数
// 无修饰
console.log(33.01 - 5) // 28.009999999999998
// 第一版本:toFixed修饰
console.log((33.01 - 5).toFixed(2)) // 28.01
// toFixed精度为3
console.log((33.01 - 5).toFixed(3)) // 28.010
// 借助 parseFloat 移除多余的0
console.log(parseFloat((33.01 - 5).toFixed(3))) // 28.010
比如要保留两位小数 那我就乘100,运算完后再除100
// 无修饰
console.log(0.05 + 0.01); // 0.060000000000000005
// 倍数处理修饰
console.log((0.05 * 100 + 0.01 * 100) / 100); // 0.06
注意:运算中,数字乘10的倍数后,必须是整数,没有小数点出现。否则,就会出现第一种情况,同样导致精度丢失。
所以这是特定场景下的解决方案,需要注意
Math.js是JavaScript和Node.js的一个广泛的数学库。它具有一个灵活的表达式解析器
npm install mathjs
or:https://mathjs.org/download.html
import {
atan2, chain, derivative, e, evaluate, log, pi, pow, round, sqrt
} from 'mathjs'
// functions and constants
round(e, 3) // 2.718
atan2(3, -3) / pi // 0.75
log(10000, 10) // 4
sqrt(-4) // 2i
pow([[-1, 2], [3, 1]], 2) // [[7, 0], [0, 7]]
derivative('x^2 + x', 'x') // 2 * x + 1
// expressions
evaluate('12 / (2.3 + 0.7)') // 4
evaluate('12.7 cm to inch') // 5 inch
evaluate('sin(45 deg) ^ 2') // 0.5
evaluate('9 / 3 + 2i') // 3 + 2i
evaluate('det([-1, 2; 3, 1])') // -7
// chaining
chain(3)
.add(4)
.multiply(2)
.done() // 14
const num = math.format(math.multiply(math.bignumber(0.5), 3)); // 0.45
const num1 = math.add(math.bignumber(0.1), math.bignumber(0.2)); // 0.3
一个小型、快速的JavaScript库,用于任意精度的十进制运算。https://npmmirror.com/package/big.js
Add Big to global scope:
<script src='path/to/big.js'></script>
ES module:
<script type='module'>
import Big from './path/to/big.mjs';
Get a minified version from a CDN:
<script src='https://cdn.jsdelivr.net/npm/big.js@6.2.1/big.min.js'></script>
$ npm install big.js
CommonJS:
const Big = require('big.js');
ES module:
import Big from 'big.js';
x = new Big(123.4567)
y = Big('123456.7e-3') // 'new' is optional
z = new Big(x)
x.eq(y) && x.eq(z) && y.eq(z) // true
返回新的结果,精度计算
0.3 - 0.1 // 0.19999999999999998
x = new Big(0.3)
x.minus(0.1) // "0.2"
x // "0.3"
链式调用
x.div(y).plus(z).times(9).minus('1.234567801234567e+8').plus(976.54321).div('2598.11772')
x.sqrt().div(y).pow(3).gt(y.mod(z)) // true
支持javascript中原生number的api
x = new Big(255.5)
x.toExponential(5) // "2.55500e+2"
x.toFixed(5) // "255.50000"
x.toPrecision(5) // "255.50"