哪个浮点比较更准确,为什么?

时间:2012-03-02 04:49:31

标签: algorithm floating-point numerical-analysis

我正在尝试使用Newton方法的不同实现来计算平方根。一个重要的决定是什么时候终止算法。

显然,使用y*yx之间的绝对差异是不行的,其中yx的平方根的当前估计值,因为对于值x可能无法以足够的精度表示其平方根。

所以我应该使用相对标准。天真的我会用这样的东西:

static int sqrt_good_enough(float x, float y) {
  return fabsf(y*y - x) / x < EPS;
}

这看起来效果很好。但是最近我开始阅读Kernighan和Plauger的编程风格元素,他们在第1章中给出了相同算法的Fortran程序,其终止标准(用C翻译)将是:

static int sqrt_good_enough(float x, float y) {
  return fabsf(x/y - y) < EPS * y;
}

两者在数学上是等价的,但是有理由选择一种形式而不是另一种吗?

2 个答案:

答案 0 :(得分:5)

他们仍然不等同;底部数字在数学上等同于fabsf(y*y - x) / (y*y) < EPS。我看到你遇到的问题是,如果y*y溢出(可能是因为x FLT_MAX而且y被选中),那么终止可能永远不会发生。以下交互使用了双打。

>>> import math
>>> x = (2.0 - 2.0 ** -52) * 2.0 ** 1023
>>> y = x / math.sqrt(x)
>>> y * y - x
inf
>>> y == 0.5 * (y + x / y)
True

编辑:作为评论(现已删除)指出,在迭代和终止测试之间共享操作也很好。

EDIT2:两者都可能存在子正常x的问题。 professionals规范化x以避免两种极端情况的并发症。

答案 1 :(得分:3)

这两个实际上并不完全等效,除非你写fabsf(y * y - x)/(y * y)&lt; EPS的第一个。 (对不起我原来评论中的拼写错误)

但我认为关键点在于使这里的表达式与您在牛顿迭代中计算y的公式相匹配。例如,如果y公式为y =(y + x / y)/ 2,则应使用Kernighan和Plauger的样式。如果是y =(y * y + x)/(2 * y),你应该使用(y * y - x)/(y * y)&lt; EPS。

通常,终止标准应该是abs(y(n + 1)-y(n))足够小(即小于y(n + 1)* EPS)。这就是两个表达式应该匹配的原因。如果它们不完全匹配,则由于不同的缩放,终止测试可能决定残差不够小而y(n)的差小于浮点误差。结果将是无限循环,因为y(n)已停止更改并且永远不会满足终止条件。

例如,下面的Matlab代码与第一个示例完全相同,但它永远运行:

x = 6.800000000000002
yprev = 0
y = 2
while abs(y*y - x) > eps*abs(y*y)
    yprev = y;
    y = 0.5*(y + x/y);
end

它的C / C ++版本存在同样的问题。