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