在 JavaScript 中, 0.1 + 0.2 === 0.3 吗? 请阐述原因并给出 解决⽅案

243 阅读2分钟

在JS 中,这道题的答案确实是 false ,⽽ 0.2 + 0.3 的结果却是 0.5 , 原因在于 JS 采⽤

IEEE 754 标准定义的 64 位浮点格式表⽰数字,所以 JS 中的所有数字都是浮点数。按照

JS 的数字格式,整数有的范围是 -2^53 ~ 2^53 ,⽽且只能表⽰有限个浮点数,能表⽰的个

数为 2^64 − 2^53 + 3 个,浮点数的个数是⽆限的,这就导致了 JS 不能精确表达所有的浮

点数,⽽只能是⼀个近似值。并且所有采⽤ IEEE 754 标准的语⾔都会有这个问题,只是

它们已经在其标准库中解决了这个问题。⽽很遗憾的是 JS 却没有。

下⾯我们来分析⼀下运算过程:

  • 0.1 的⼆进制表⽰为 1.1001100110011001100110011001100110011001100110011001 1(0011)+ *

2^-4 ;

  • 当 64bit 的存储空间⽆法存储完整的⽆限循环⼩数,⽽ IEEE 754 Floating-point(双精

度) 采⽤ round to nearest, tie to even 的舍⼊模式,因此 0.1 实际存储时的位模式是

0-01111111011-1001100110011001100110011001100110011001100110011010 ;

  • 0.2 的⼆进制表⽰为 1.1001100110011001100110011001100110011001100110011001 1(0011)+ *

2^-3 ;

  • 当 64bit 的存储空间⽆法存储完整的⽆限循环⼩数,⽽ IEEE 754 Floating-point 采⽤

round to nearest, tie to even 的舍⼊模式,因此 0.2 实际存储时的位模式是 0-

01111111100-1001100110011001100110011001100110011001100110011010 ;

实际存储的位模式作为操作数进⾏浮点数加法,得到 0-01111111101-

0011001100110011001100110011001100110011001100110100 。转换为⼗进制即为

0.30000000000000004 。

1.原⽣的解决方法如下:

parseFloat((0.1 + 0.2).toFixed(10))

2.更精确的解决方案如下::

const accAdd = (arg1, arg2) => {
        let r1, r2, m;
        try {
            r1 = arg1.toString().split(".")[1].length
        } catch (e) {
            r1 = 0
        }
        try {
            r2 = arg2.toString().split(".")[1].length
        } catch (e) {
            r2 = 0
        }
        m = Math.pow(10, Math.max(r1, r2))
        return (parseInt(arg1 * m, 10) + parseInt(arg2 * m, 10)) / m
    }