你的
left
和
right
不是“几乎相等”,因为它们之间的距离太远了,比
AlmostEquals
的默认公差要大。你链接到的问题中的一个答案显示了4 ULP的公差,但你的数字相差14 ULP(使用IEEE 754 32位二进制和正确舍入软件)。 (ULP是浮点值的最小增量。对于小幅度浮点数来说很小,对于大数来说很大,因此它与数字的大小大约成正比。)
在执行任何浮点比较之前,你应该清楚地了解你正在比较的值中可能存在的错误以及你正在执行的比较。
人们经常错误地说你不能测试浮点值是否相等。这是错误的;执行
a == b
是一个完美的操作。如果且仅当
a
等于
b
时(即,
a
和
b
是具有完全相同值的数字),它将返回true。实际问题是,他们试图计算给定不正确输入的正确函数。==是一个函数:它接受两个输入并返回一个值。显然,如果你给出任何函数不正确的输入,它可能会返回不正确的结果。因此,这里的问题不是浮点比较;而是不正确的输入。你通常无法在给定不正确的输入的情况下正确计算总和,乘积,平方根,对数或任何其他函数。因此,在使用浮点时,必须设计一种算法来处理近似值(或在特殊情况下,非常小心地确保不引入错误)。
通常,人们尝试通过接受略有不同的数字作为相等的数字来解决其浮点值中的错误。这会减少误报负数(由于先前的计算错误而导致的不等指示),但代价是增加误报正数(由于松散接受而导致的相等指示)。是否可以接受这种一种错误与另一种错误的交换取决于应用程序。没有通用的解决方案,这就是为什么像
AlmostEquals
这样的函数通常很糟糕的原因。
浮点值中的错误是前面操作和值的结果。这些错误可能从零到无穷大,具体取决于情况。因此,你永远不应该简单地接受诸如
AlmostEquals
之类的函数的默认公差。相反,应该计算公差,这是特定于他们的应用程序、需求和计算的,并使用该计算公差(或根本不使用比较)。
另一个问题是,诸如
AlmostEquals
的函数通常使用相对于被比较的值指定的公差编写。然而,值中的错误可能已经受到远不同数量级的中间值的影响,因此最终误差可能是与未出现在被比较的值中的数据有关的函数。
在测试其他代码时,“近似”的浮点比较可能是可以接受的,因为大多数错误很可能会导致大误差,因此对等式的宽松接受将允许良好的代码继续运行,但会报告大多数错误的代码。然而,即使在这种情况下,您也必须适当地设置预期结果和允许的误差容限。 AlmostEquals
代码似乎将误差容限硬编码。