手记

使用“机械极小值(machine epsilon)”作为比较的 容差 处理JS中小数的比较

内容来自: You-dont-know-JS中类型与文法的第二章(值)

小数值

使用二进制浮点数的最出名(臭名昭著)的副作用是(记住,这是对 所有 使用 IEEE 754 的语言都成立的 —— 不是许多人认为/假装 仅 在 JavaScript 中存在的问题):

0.1 + 0.2 === 0.3; // false1

从数学的意义上,我们知道这个语句应当为 true。为什么它是 false

简单地说,0.10.2 的二进制表示形式是不精确的,所以它们相加时,结果不是精确地 0.3。而是 非常 接近的值:0.30000000000000004,但是如果你的比较失败了,“接近”是无关紧要的。

注意: JavaScript 应当切换到可以精确表达所有值的一个不同的 number 实现吗?有些人认为应该。多年以来有许多选项出现过。但是没有一个被采纳,而且也许永远也不会。它看起来就像挥挥手然后说 “已经改好那个 bug 了!” 那么简单,但根本不是那么回事儿。如果真有这么简单,它绝对在很久以前就被改掉了。

现在的问题是,如果一些 number 不能被 信任 为精确的,这不是意味着我们根本不能使用 number 吗? 当然不是

在一些应用程序中你需要多加小心,特别是在对付小数的时候。还有许多(也许是大多数?)应用程序只处理整数,而且,最大只处理到几百万到几万亿。这些应用程序使用 JS 中的数字操作是,而且将总是,非常安全 的。

要是我们 确实 需要比较两个 number,就像 0.1 + 0.20.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


0人推荐
随时随地看视频
慕课网APP