为什么0.1+0.2 !==0.3?如何解决?

169 阅读2分钟

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

js数字存储

与许多其他编程语言不同,JavaScript 并未定义不同类型的数字数据类型,而是始终遵循国际 IEEE 754 标准,将数字存储为双精度浮点数。我们可以无需知道他的存储形式,只需要简单的理解成就是存储一个数值时,所使用的二进制位数比较多而已,这样得到的数会更加精确。

image.png

image.png

而对于像0.1这样的数字用二进制表示你就会发现无法整除,最后算下来会是 0.000110011……由于存储空间有限,最后计算机会舍弃后面的数字,所以我们最后就只能得到一个近似值。

在js中如果这个近似值足够近似,那么js就会认为他就是那个值

0.1+0.2 !==0.3

现在,我们知道了计算机是通过二进制的方式存储数据的,所以计算机计算0.1+0.2的时候,实际上是计算的两个数的二进制的和,0.1的二进制是0.0001100110011001100...(1100循环),0.2的二进制是:0.00110011001100...(1100循环),这两个数的二进制都是无限循环的数。
js就会根据它的规则去处理无限循环的小数
在二进制科学表示法中,双精度浮点数的小数部分最多只能保留52位,再加上前面的符号位,其实就是保留53位有效数字,剩余的舍去,遵从“0舍1入”的原则。

所以0.1+0.2,先进行二进制的和,在转位10进制,最后得出0.30000000000000004
所以,我们做项目的时候,如果有小数相加,没有做特殊的处理,然后去做判断,就会踩这样的坑,那我们需要怎么去解决这个问题呢?

如何解决

如何实现0.1+0.2=0.3呢
最好的方法是设置一个误差范围值,通常称为”机器精度“,而对于Javascript来说,这个值通常是2^-52,

ES6为我们提供了一个这样的值:Number.EPSILON, 这个值非常非常小,在底层计算机已经帮我们运算好

Number.EPSILON
// 2.220446049250313e-16

Number.EPSILON的实质是一个可以接受的最小误差范围。

我们只要判断(0.1+0.2)-0.3小于Number.EPSILON,在这个误差的范围内就可以判定0.1+0.2===0.3为true。

function withinErrorMargin (left, right) {
  return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}

0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true

1.1 + 1.3 === 2.4 // false
withinErrorMargin(1.1 + 1.3, 2.4) // true