chdyiboke / weekly

issue and share weekly
5 stars 1 forks source link

0.1 + 0.2 !== 0.3 #52

Open liukexina opened 3 years ago

liukexina commented 3 years ago
alert( 0.1 + 0.2 == 0.3 ); // false
alert( 0.1 + 0.2 ); // 0.30000000000000004

为什么?

一个数字以其二进制的形式存储在内存中,一个 1 和 0 的序列。但是在十进制数字系统中看起来很简单的 0.1,0.2 这样的小数,实际上在二进制形式中是无限循环小数。

换句话说,什么是 0.1?0.1 就是 1 除以 10,1/10,即十分之一。在十进制数字系统中,这样的数字表示起来很容易。将其与三分之一进行比较:1/3。三分之一变成了无限循环小数 0.33333(3)。

在十进制数字系统中,可以保证以 10 的整数次幂作为除数能够正常工作,但是以 3 作为除数则不能。也是同样的原因,在二进制数字系统中,可以保证以 2 的整数次幂作为除数时能够正常工作,但 1/10 就变成了一个无限循环的二进制小数。

使用二进制数字系统无法 精确 存储 0.1 或 0.2,就像没有办法将三分之一存储为十进制小数一样。

liukexina commented 3 years ago

当我们对两个数字进行求和时,它们的“精度损失”会叠加起来

解决: 最可靠的方法是借助方法 toFixed(n) 对结果进行舍入

let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // 0.30

我们可以将数字临时乘以 100(或更大的数字),将其转换为整数,进行数学运算,然后再除回。当我们使用整数进行数学运算时,误差会有所减少,但仍然可以在除法中得到:

alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001

结论: 完全避免小数处理几乎是不可能的。只需要在必要时剪掉其“尾巴”来对其进行舍入即可。