内容来自: You-dont-know-JS中类型与文法的第二章(值)
小数值
使用二进制浮点数的最出名(臭名昭著)的副作用是(记住,这是对 所有 使用 IEEE 754 的语言都成立的 —— 不是许多人认为/假装 仅 在 JavaScript 中存在的问题):
0.1 + 0.2 === 0.3; // false1
从数学的意义上,我们知道这个语句应当为 true
。为什么它是 false
?
简单地说,0.1
和 0.2
的二进制表示形式是不精确的,所以它们相加时,结果不是精确地 0.3
。而是 非常 接近的值:0.30000000000000004
,但是如果你的比较失败了,“接近”是无关紧要的。
注意: JavaScript 应当切换到可以精确表达所有值的一个不同的 number
实现吗?有些人认为应该。多年以来有许多选项出现过。但是没有一个被采纳,而且也许永远也不会。它看起来就像挥挥手然后说 “已经改好那个 bug 了!” 那么简单,但根本不是那么回事儿。如果真有这么简单,它绝对在很久以前就被改掉了。
现在的问题是,如果一些 number
不能被 信任 为精确的,这不是意味着我们根本不能使用 number
吗? 当然不是。
在一些应用程序中你需要多加小心,特别是在对付小数的时候。还有许多(也许是大多数?)应用程序只处理整数,而且,最大只处理到几百万到几万亿。这些应用程序使用 JS 中的数字操作是,而且将总是,非常安全 的。
要是我们 确实 需要比较两个 number
,就像 0.1 + 0.2
与 0.3
,而且知道这个简单的相等测试将会失败呢?
可以接受的最常见的做法是使用一个很小的“错误舍入”值作为比较的 容差。这个很小的值经常被称为“机械极小值(machine epsilon)”,对于 JavaScript 来说这种 number
通常为 2^-52(2.220446049250313e-16)
。
在 ES6 中,使用这个容差值预定义了 Number.EPSILON
,所以你将会想要使用它,你也可以在前 ES6 中安全地填补这个定义:
if (!Number.EPSILON) { Number.EPSILON = Math.pow(2,-52); }123
我们可以使用这个 Number.EPSILON
来比较两个 number
的“等价性”(带有错误舍入的容差):
function numbersCloseEnoughToEqual(n1,n2) { return Math.abs( n1 - n2 ) < Number.EPSILON; }var a = 0.1 + 0.2;var b = 0.3; numbersCloseEnoughToEqual( a, b ); // truenumbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false123456789
可以被表示的最大的浮点值大概是 1.798e+308
(它真的非常,非常,非常大!),它为你预定义为 Number.MAX_VALUE
。在极小的一端,Number.MIN_VALUE
大概是 5e-324
,它不是负数但是非常接近于0
!