在 F# 中。如何有效地比较几乎相等的浮点数?它也应该适用于非常大和非常小的值。我正在考虑首先比较指数,然后比较有效位(尾数),同时忽略其 52 位的最后 4 位。这是一个好方法吗?如何获得浮点数的指数和有效数?
2 回答
当您询问如何比较几乎相等的浮点值时,您是在问:
- 我有两个值
x
和y
,它们是用浮点算术计算的,因此它们包含舍入误差并且是理想数学值x和y的近似值。如何使用浮点x
并y
比较数学x和y是否相等?
这里有两个问题:
x
我们不知道或中可能有多少错误y
。一些算术组合会放大误差,而另一些组合会缩小误差。误差范围可能从x
零y
到无穷大,您没有向我们提供任何有关此的信息。- 通常假设目标是在不相等但彼此接近
x
时产生“相等”的结果。y
这会将假阴性(即使数学x和y相等也会报告不等式)转换为阳性。但是,它会产生误报(即使数学x和y不相等,也会报告相等)。
这些问题没有通用的解决方案。
- 在不知道有关该应用程序的具体细节的情况下,一般不可能知道应用程序是否可以容忍在值不相等时被告知值相等,反之亦然。
- 通常不可能
x
知道和中可能存在多少误差y
。
因此,对于已近似计算的值的相等性,没有正确的一般测试。
请注意,这个问题实际上并不是关于测试是否相等。通常,不可能计算出错误数据的任何函数(常量函数等微不足道的函数除外)。由于x
并且y
包含错误,因此不可能使用无错误的x
方式计算 log( x ),或者无错误地计算 arccos( y ) 或 sqrt( x )。事实上,如果误差在不y
大于 1 时略大于 1,y
或者在xx
不是时略小于零,那么即使理想的数学值可以正常工作,计算or也会产生异常和 NaN。acos(y)
sqrt(x)
这一切意味着您不能简单地将精确的数学算术转换为近似的浮点算术并期望得到一个好的结果(无论您是否正在测试相等性)。您必须考虑将精确算术转换为近似算术的影响,并评估它们如何影响您的程序和数据。浮点算术的使用,包括相等的比较,必须根据具体情况进行调整。
F#float
只是System.Double
. 在这种情况下,您可以使用BitConverter.DoubleToInt64Bits方法有效(且安全!)将 F#float
值“转换”为int64
; 这很有用,因为它避免了分配 a byte[]
,正如约翰在他的评论中提到的那样。您可以int64
使用一些简单的按位运算从中获得指数和有效数。
正如约翰所说,您可能最好通过简单的相对准确性检查。它可能是最快的解决方案,并且对于许多用例来说“足够接近”(例如,检查迭代求解器是否已经收敛到一个解决方案)。如果您需要特定数量的准确度,请查看 NUnit 的代码——它有一些很好的 API 用于断言值在预期值的某个百分比或 ulps 数内。