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表示1Number可表示的大于1最小的浮点数之间的差值。

Number.EPSILON的值为2^-52

Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON 
阅读剩余
THE END