在JavaScript中为神马0.1+0.2 不等于 0.3, 并如何解决

180 阅读2分钟

原因

在js中执行 0.1+0.2的结果是0.30000000000000004。这样的原因是什么呢?

  • 这是因为js数值的精度问题导致。JS的Number类型遵循 IEEE 754标准,使用的是64位固定长度来表示。 在计算的过程中转换过程经历三个阶段:

    1. 将0.1转成二进制。
    2. 将转换后的二进制通过科学计数法表示。
    3. 通过科学计数法表示的二进制转换成IEEE 754标准表示
  • 经过上面的阶段的二进制由于小数点的转换成二进制的规则中会在最后一位四舍五入。导致了原来的值发生了变化。详细情况如下:

    1. 一个数的小数部分怎么转成二进制的:⼀个数的⼩数部分,乘以 2,然后取整数部分的结果,再⽤计算后的⼩ 数部分重复计算,直到⼩数部分为 0 。 因此 0.1 转换为⼆进制表示的过程如下:

    image.png 得到 0.1 的⼆进制表示为 0.0001100110011...(⽆限重复 0011)

    1. 通过科学计数法表示

    0.00011...(⽆限重复 0011) 通过科学计数法表示则是 1.10011001...(⽆线重复 1001)*2

    1. 再经过IEEE 754标准转换(此处不需要过分解析)后,0.1的二进制已经变成了

      0 011 1111 1011 1001...( 11 x 1001)...1010 (sign bit) (exponent bias) (fraction)

    (请注意最后四位,是 1010 ⽽不是 1001,因为四舍五⼊有进位,这个进位 就是造成 0.1 + 0.2 不等于 0.3 的原因)

    此时如果将这个数转换为⼗进制,可以发现值已经变为 0.100000000000000005551115123126 ⽽不是 0.1 了,因此这 个计算精度就出现了问题。

解决

1. 我们可以通过js提供的Number.EPSLON方法来解决此问题:
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON 时我们就可以认为俩值是相等的。

2. Number.EPSILON可能会存在兼容性的问题:
if(Number.EPSILON === undefined){
    Number.EPSILON = Math.pow(2,-52)
}