深度剖析0.1 +0.2 !== 0.3

2,177 阅读2分钟

一、0.1+0.2 !== 0.3 这是为什么呢??

我们都知道浮点数计算是不精确的,0.1 + 0.2 的返回式实际上是这样的:

0.1 + 0.2 = 0.30000000000000004


那么这个又是如何计算出来的呢,我们都知道在计算机里面任何东西都是二进制存在的,如果不限精度的话。

0.1 转换成二进制是 0.000110011001100110011无限循环)

0.2 转换成二进制是 0.0011001100110011(0011无限循环)

既然0.1和0.2转成二进制的都是无限循环小数,那么0.1 + 0.2 = 0.30000000000000004是如何计算出来的呢?

在JS中的Number类型,二进制小数的有效位数只有52位,从0到51位(包括边界)

在js中是如上定义的,所以我们在 控制台中输出

0.1.toString('2')

0.001100110011001100110011001100110011001100110011001101

0.2.toString('2') 

0.0001100110011001100110011001100110011001100110011001101

所以如下公式

0.001100110011001100110011001100110011001100110011001101

+

0.0001100110011001100110011001100110011001100110011001101

=

0.0100110011001100110011001100110011001100110011001100



0.0100110011001100110011001100110011001100110011001100正是0.300000000000000004(17位十进制数)的二进制形式。


这就是0.1 + 0.2 = 0.30000000000000004的原因。


二、如何使0.1+0.2 === 0.3

那么应该怎样来解决0.1+0.2等于0.3呢? 最好的方法是设置一个误差范围值,通常称为”机器精度“,而对于Javascript来说,这个值通常是2^-52,而在ES6中,已经为我们提供了这样一个属性:Number.EPSILON,而这个值正等于2^-52。这个值非常非常小,在底层计算机已经帮我们运算好,并且无限接近0,但不等于0,。这个时候我们只要判断(0.1+0.2)-0.3小于Number.EPSILON,在这个误差的范围内就可以判定0.1+0.2===0.3为true。

function numberepsilon(arg1, arg2){
        return Math.abs(arg1 - arg2) < Number.EPSILON;
}
console.log(numberepsilon(0.1 + 0.2, 0.3));

输出 true

在这里我们需要考虑兼容性的问题了,在FireFox, Chrome中支持这个属性,但是IE并不支持(IE10以下版本不兼容),所以我们还要解决IE的不兼容问题。

Number.EPSILON=(function(){ //解决兼容性问题
       return Number.EPSILON?Number.EPSILON:Math.pow(2,-52);
})();


解决方法:我们通过一个自调用函数,当JS文件刚加载到内存中,就会去判断并返回一个结果,这种代码更节约性能,也更美观。


结束语: 做一个努力、勤奋、主动的前端工程师