0.1+0.2!=0.3?
在 JavaScript 中,如果判断0.1 + 0.2 === 0.3
,结果是false
,就像这样:
console.log(0.1 + 0.2 === 0.3); // false
console.log(0.1 + 0.2); // 0.30000000000000004
JavaScript 采用的是IEEE754的64位双精度
版本
浮点数转二进制的过程如下:
整数部分采用 /2
取余法
3 => 3/2 = 1 余 1
1 => 1/2 = 0 余 1
所以 3(十进制)= 11(二进制)
小数部分采用 *2
取整法
0.1 => 0.1*2 = 0.2 取整 0
0.2 => 0.2*2 = 0.4 取整 0
0.4 => 0.4*2 = 0.8 取整 0
0.8 => 0.8*2 = 1.6 取整 1
0.6 => 0.6*2 = 1.2 取整 1
0.2 => 0.2*2 = 0.4 取整 0
0.4 => 0.4*2 = 0.8 取整 0
0.8 => 0.8*2 = 1.6 取整 1
0.6 => 0.6*2 = 1.2 取整 1
...发生循环
得到结果 0.1(十进制)= 00011001100110011001100110011... (0011)循环(二进制)
同理,既有整数又有小数的数值进行二进制转换,就是分别对整数和小数部分进行二进制转换,再相加即可。
上面的例子中可以看到 0.1
转二进制会发生无限循环,而 IEEE 754
标准中的尾数位只能保存 52 位
有效数字,所以 0.1
转二进制就会发生舍入,所以就产生了误差。
- 十进制浮点数转换二进制后尾数的
52 位
有效数字是从第一个1
开始向后保留52 位
有效数字,所以接下来你会发现0.1
和0.2
保留52 位
尾数后长度会不同。 - 在舍入的过程中,遵循
0 舍 1 入
的规则。第 53 位为 1 就+1,为 0 则不进位
接下来我们一起看一下示例中的运算过程:
0.1
转二进制
0.0001100110011001100110011001100110011001100110011001100110011
保留52位尾数
0.00011001100110011001100110011001100110011001100110011010
0.2
转二进制
0.001100110011001100110011001100110011001100110011001100110011
保留52位尾数
0.0011001100110011001100110011001100110011001100110011010
进行相加
0.00011001100110011001100110011001100110011001100110011010
0.0011001100110011001100110011001100110011001100110011010
----------------------------------------------------------
0.01001100110011001100110011001100110011001100110011001110
相加后的结果保留52位尾数
0.010011001100110011001100110011001100110011001100110100
转十进制
0.30000000000000004
解决办法
在 JavaScript 中,可用Number.EPSILON
解决。
Number.EPSILON
表示1
与Number
可表示的大于1
的最小的浮点数之间的差值。
Number.EPSILON
的值为2^-52
。
Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON