浮点数的精度有限。根据语言和体系结构,它们通常使用 32 位 (float) 或 64 位 (double,“双精度”) 表示。尽管在 JavaScript 这样的无类型语言中事情变得模糊不清,但在这一切之下仍然有一台实际的机器,这台机器必须执行浮点运算。
问题在于,由于精度有限,某些计算的结果无法准确表示。这通过Wikipedia page about floating point artithmetic 上的一些示例进行了解释。
对于想要了解所有细节的人,通常推荐有关What Every Computer Scientist Should Know About Floating-Point Arithmetic 的文章。但说真的:并不是每个计算机科学家都需要了解所有这些,而且我很确定世界上只有少数人真正阅读整本书......
作为一个过度暗示的示例:假设您有 5 位数字来存储一个数字。当您添加类似
10000.
+ 0.00001
--------------------
= 10000.
.00001 部分基本上会被“截断”,因为它不适合 5 位数字。
(这不完全是这样的工作原理,但应该让这个想法得到理解)
Number.EPSILON的实际值,根据the documentation,约为2.22 * 10-16,是“1与大于的最小浮点数之差1"。 (这有时称为ULP, Unit In The Last Place)。
因此将此值添加到 1.0 将产生不同的数字。但是将它添加到 2.5 不会 得到不同的数字,因为 2.5 和大于 2.5 的最小浮点数之间的差大于这个 epsilon。因此 epsilon 被截断,就像上面示例中的 .00001。
某些语言/库可能提供“ulp”函数,该函数返回给定值与下一个更大的可表示值之间的差值。例如,在 Java 中,您有
System.out.println(Math.ulp(1.0)); // Prints 2.220446049250313E-16
System.out.println(Math.ulp(2.5)); // Prints 4.440892098500626E-16
第一个显然是存储在Number.EPSILON 中的内容。第二个是添加到 2.5 时应该产生不同值的值。所以
-
2.5 < 2.5 + 4.4408E-16 将是 false 和
-
2.5 < 2.5 + 4.4409E-16 将是 true