先看如下例子:
var p1 = 0.1 + 0.2
console.log(p1); // 0.30000000000000004
console.log(p1.toString(2)); // 0.0100110011001100110011001100110011001100110011001101
var p2 = 1.5 - 1.4
console.log(p2); // 0.10000000000000009
console.log(p2.toString(2)); // 0.000110011001100110011001100110011001100110011001101
var p3 = 0.2 * 3
console.log(p3); // 0.6000000000000001
console.log(p3.toString(2)); // 0.100110011001100110011001100110011001100110011001101
var p4 = 0.3 / 0.1
console.log(p4); // 2.9999999999999996
console.log(p4.toString(2)); // 10.111111111111111111111111111111111111111111111111111
计算结果和我们想的不一样,导致出现这种情况的原因,是因为在 JavaScript 中数字只有一种类型 number 采用双精度浮点数存储,遵循 IEEE 754 规范,使用64个比特位存储。
64比特位分为三个部分:
- 1位: 符号位,表示整个数的正负值,0表示正,1表示负
- 11位: 指数位,用来表示次方数
- 52位:小数位,用来表示精确度, 超出的部分自动进一舍零,最后52位
上例代码中 0.1+0.2 的二进制为
0100110011001100110011001100110011001100110011001101...(1001无限循环)
然后小数位的长度为52,截取后转为十进制就成了 0.30000000000000004,造成了误差
经常出现
// 浮点数计算
console.log(0.1 + 0.2 == 0.3);
// toFixed 不进行四舍五入 // false
console.log(1.335.toFixed(2));
// 大整数计算 // 1.33
console.log(9999999999999999 == 10000000000000001); // true
// 长数值转换
console.log(parseFloat(9999999999999999.9)); // 10000000000000000
console.log(parseInt(9999999999999999)); // 10000000000000000
console.log(parseFloat(9.999999999999999)); // 9.999999999999998
解决方案
可以通过 mathjs、decimal.js、bignumber 等这种数学库,来操作计算。