现在每日的 Javascript 提问时间,已经成为了部门前端开发人员的下午茶。这次的问题是:

请问下面的代码会弹出 true or false ?
<script type="text/javascript">
    var n1 = 0.1;
    var n2 = 0.2;
    var n3 = 0.4;
    var n4 = 0.8;

    alert((n1+n2+n3+n4) === (n1+n2)+(n3+n4));
</script>

https://friable.rocks/_/2008_07_07/1215423870.png

这是个很「诡异」的问题。乍看之下应该都是 true,不过「现实是残酷的」,这段代码在各浏览器中的运行结果如上图。

下面的解释还是能让人接受的:「简单来说就是二进制的浮点数难以正确处理十进制的小数」。

其实浮点数的精度问题,在大部分的开发语言中都能碰到(原谅我绝对了)。比如 PHP 中执行

<?php
    $a = 0.100000000000000000000000000000001;
    var_dump($a + 0.000000000000000001 == $a + 0.000000000000000002);
?>

以及 Java 中运行这样的代码

public static void main(String[] args) {
    System.out.print(0.1 + 0.2);
}

也会出现类似的问题(虽然例子可能有点极端,不过已经能够说明问题)。

那么,该如何解决该问题?「解铃还需经理人」,小马 同学在讲解答案的时候,给出了部分解决代码

Math.formatFloat = function(f, digit) {
    var m = Math.pow(10, digit);
    return parseInt(f * m, 10) / m;
}

alert(Math.formatFloat(0.1 + 0.2, 1));  // 0.3

玉伯的补充:

关于精度问题,一般涉及到小数点时,如果不是0.5, 0.25 这种反复乘以 2 会等于 1 的小
数,其它小数都无法无二进制精确表示,由此造成了误差。比如 10 进制的 0.1, 用二进
制表示是 0.0001100110011001100110...........  无限循环了。

对于进制方面更深一层的了解,请 参阅这里

那么,下面的测试

alert((0.1 + 0.2 + 0.4 + 0.8)    === ((0.1 + 0.2) + (0.4 + 0.8))); // 已经知道是false
alert((0.1 + 0.2 + 0.4 + 0.8)    === ((0.1 + 0.8) + (0.2 + 0.4)));    
alert((0.25 + 0.75 + 0.05 + 1.0) === (0.25 + (0.75 + 0.05) + 1.0));
alert((0.25 + 0.75 + 0.05 + 1.0) === ((0.25 + 0.75) + (0.05 + 1.0)));
alert((0.25 + 0.75 + 0.05 + 1.0) === ((1.0 + 0.75) + (0.05 + 0.25)));

其它的 alert 结果是什么?