机器epsilon的使用是否适合浮点相等测试?

时间:2010-07-19 13:03:51

标签: language-agnostic floating-point epsilon precision

这是Testing for floating-point value equality: Is there a standard name for the “precision” constant?的后续行动 有一个非常相似的问题Double.Epsilon for equality, greater than, less than, less than or equal to, greater than or equal to


众所周知,两个浮点值 x y 的相等测试看起来应该更像这样(而不是简单的=):

  

abs( x - y )< epsilon ,其中 epsilon 是一个非常小的值。

如何为 epsilon 选择一个值?

显然最好选择 epsilon 作为尽可能小的值,以获得相等检查的最高精度。

例如,.NET框架提供了一个常量System.Double.Epsilon(= 4.94066×10 -324 ),它表示大于System.Double的最小值System.Double.Epsilon零。

然而,事实证明这个特定值不能可靠地用作 epsilon ,因为:

  

0 + System.Double.Epsilon≠0

     

1 + double = 1(!)

,如果我理解正确,因为该常数小于machine epsilon

<击> →这是正确的吗?

→这是否也意味着我可以可靠地使用 epsilon:= machine epsilon 进行相等测试?

  

删除了这两个问题,因为它们已经与上面链接的第二个SO问题得到了充分的回答。


链接到维基百科的文章说,对于64位浮点数(即许多语言中的{{1}}类型),机器epsilon等于:

  

2 -53 ,或约。 0.000000000000000111(小数点后15个零的数字)

→是否遵循这一点,所有64位浮点值都保证精确到14(如果不是15)数字?

4 个答案:

答案 0 :(得分:10)

  

如何选择epsilon的值?

简答:您选择的小值适合您的应用需求。

长答案:没有人能够知道您的应用程序执行了哪些计算以及您希望结果的准确。由于舍入错误总结机器epsilon几乎所有时间都太大,所以你必须选择自己的值。根据您的需要,0.01就足够了,或者可能是0.00000000000001或更少。

问题是,你真的想要/需要对浮点值进行相等测试吗?也许你应该重新设计你的算法。

答案 1 :(得分:5)

在过去,当我不得不使用epsilon值时,它比机器epsilon值大得多。

虽然是32位双打(而不是64位双打),但我们发现大多数(如果不是全部)计算值都需要10 -6 的epsilon值特殊应用

您选择的epsilon的值取决于您的数字的比例。如果您正在处理非常大(10 +10 说),那么您可能需要更大的epsilon值,因为您的有效数字不会延伸到分数很远部分(如果有的话)。如果您正在处理非常小(10 -10 ),那么显然您需要一个小于此值的epsilon值。

您需要进行一些实验,执行计算并检查输出值之间的差异。只有当您知道潜在答案的范围时,您才能确定适合您应用的

答案 2 :(得分:4)

可悲的事实是:浮点比较没有合适的epsilon。如果你不想遇到严重的错误,请使用另一种方法进行浮点相等测试。< / p>

近似浮点比较是一个非常棘手的领域,abs(x - y) < eps方法仅适用于非常有限的值范围,主要是因为绝对差异没有考虑比较值的大小,但是也是由于在使用不同指数减去两个浮点值时发生显着的数字消除。

有更好的方法,使用相对差异或ULP,但它们有自己的缺点和陷阱。阅读Bruce Dawson的优秀文章Comparing Floating Point Numbers, 2012 Edition,深入了解棘手的浮点比较究竟是什么 - 对于任何进行浮点编程的人来说都是必读的恕我直言!我确信无数成千上万的人用于找出由于天真的浮点比较而产生的细微错误。

答案 3 :(得分:2)

我也对正确的程序有疑问。不过我相信应该这样做:

abs(x - y) <= 0.5 * eps * max(abs(x), abs(y))

而不是:

abs(x - y) < eps

出现这种情况的原因来自机器epsilon的定义。使用python代码:

import numpy as np
real = np.float64
eps = np.finfo(real).eps

## Let's get the machine epsilon
x, dx = real(1), real(1)
while x+dx != x: dx/= real(2) ;

print "eps = %e  dx = %e  eps*x/2 = %e" % (eps, dx, eps*x/real(2))

给出:eps = 2.220446e-16 dx = 1.110223e-16 eps*x/2 = 1.110223e-16

## Now for x=16
x, dx = real(16), real(1)
while x+dx != x: dx/= real(2) ;

print "eps = %e  dx = %e  eps*x/2 = %e" % (eps, dx, eps*x/real(2))

现在提供:eps = 2.220446e-16 dx = 1.776357e-15 eps*x/2 = 1.776357e-15

## For x not equal to 2**n
x, dx = real(36), real(1)
while x+dx != x: dx/= real(2) ;

print "eps = %e  dx = %e  eps*x/2 = %e" % (eps, dx, eps*x/real(2))

返回:eps = 2.220446e-16 dx = 3.552714e-15 eps*x/2 = 3.996803e-15

然而,尽管dx和eps * x / 2之间存在差异,但我们看到dx <= eps*x/2, 因此,它用于相等性测试,在测试数值程序中的收敛时检查公差等等。

这类似于: www.ibiblio.org/pub/languages/fortran/ch1-8.html#02, 但是,如果有人知道更好的程序或者这里有什么不对,请说。