在JS 中,这道题的答案确实是 false ,⽽ 0.2 + 0.3 的结果却是 0.5 , 原因在于 JS 采⽤
IEEE 754 标准定义的 64 位浮点格式表⽰数字,所以 JS 中的所有数字都是浮点数。按照
JS 的数字格式,整数有的范围是 -2^53 ~ 2^53 ,⽽且只能表⽰有限个浮点数,能表⽰的个
数为 2^64 − 2^53 + 3 个,浮点数的个数是⽆限的,这就导致了 JS 不能精确表达所有的浮
点数,⽽只能是⼀个近似值。并且所有采⽤ IEEE 754 标准的语⾔都会有这个问题,只是
它们已经在其标准库中解决了这个问题。⽽很遗憾的是 JS 却没有。
下⾯我们来分析⼀下运算过程:
- 0.1 的⼆进制表⽰为 1.1001100110011001100110011001100110011001100110011001 1(0011)+ *
2^-4 ;
- 当 64bit 的存储空间⽆法存储完整的⽆限循环⼩数,⽽ IEEE 754 Floating-point(双精
度) 采⽤ round to nearest, tie to even 的舍⼊模式,因此 0.1 实际存储时的位模式是
0-01111111011-1001100110011001100110011001100110011001100110011010 ;
- 0.2 的⼆进制表⽰为 1.1001100110011001100110011001100110011001100110011001 1(0011)+ *
2^-3 ;
- 当 64bit 的存储空间⽆法存储完整的⽆限循环⼩数,⽽ IEEE 754 Floating-point 采⽤
round to nearest, tie to even 的舍⼊模式,因此 0.2 实际存储时的位模式是 0-
01111111100-1001100110011001100110011001100110011001100110011010 ;
实际存储的位模式作为操作数进⾏浮点数加法,得到 0-01111111101-
0011001100110011001100110011001100110011001100110100 。转换为⼗进制即为
0.30000000000000004 。
1.原⽣的解决方法如下:
parseFloat((0.1 + 0.2).toFixed(10))
2.更精确的解决方案如下::
const accAdd = (arg1, arg2) => {
let r1, r2, m;
try {
r1 = arg1.toString().split(".")[1].length
} catch (e) {
r1 = 0
}
try {
r2 = arg2.toString().split(".")[1].length
} catch (e) {
r2 = 0
}
m = Math.pow(10, Math.max(r1, r2))
return (parseInt(arg1 * m, 10) + parseInt(arg2 * m, 10)) / m
}