JS经典问题:0.1+0.2!==0.3

177 阅读2分钟

先说解决方案:用Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON进行比较

IEEE 754 64浮点类型这里不做赘述,只单纯解释上述代码。

在ES6中,Number对象上新增了一个常量Number.EPSILON,表示1和大于1的最小浮点数之间的差,相当于2的-52次方,那么这个常量就代表JS能够表示的最小精度,如果两个浮点数的差小于在这个值,我们就认为这两个浮点数相等。

进一步延伸:我们可以用Number.EPSILON来设置误差范围。

例如:我们将误差范围设置为2的-50次方,那么上面的解决方案可以写成:

Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON * Math.pow(2, 2)

进一步看Number.EPSILON,他的值打印出来是2.220446049250313e-16,其中的e代表科学计数法,是exponent(指数)的缩写,在浏览器中,小于1e-7的数字会自动转成科学计数法

image.png

那么在实际编程中可能需要我们强制转换出来,可以用toFixed把字符补全,再去掉后面的0:

var a = 1.1e-7;
a.toFixed(10).replace(/\.?0+$/, '');

上述代码toFixed的参数10,其实可以是任意比8大的数字,因为1.1后有一位小数,e后为-7,加在一起则得到a的精度是小数点后8位,所以如果直接把toFixed的参数写成8即可得到结果:

image.png

这样显然不能适配所有情况,所以要封装成通用函数的话,需要进一步优化,我的方案是将科学计数法的数字拆出来,确定小数点后有几位,e后面有几位,随便写一个例子:

image.png

根据上图,可以直接用fixed1[1].length加上fixed1[2]可以得出这个数字的整体精度,然后直接写入toFixed的参数即可,当然需要把科学计数法的数字转成字符串,fixed1[1]和fixed[2]也得转成数字。封装函数的话还需要一些前置判断,比如判断入参是否是数字或NaN,等等。这里不贴代码了,如果搜科学计数法转数字,应该会得到很多大神写出的非常完善的方法,这只是我在0.1+0.2!==0.3的问题上的一个小小的探索。

希望对看到文章的人有帮助,或者有更好的将科学计数法转数字的方法可评论讨论。