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

Algorithm 哪个浮点比较更准确,为什么?,algorithm,floating-point,numerical-analysis,Algorithm,Floating Point,Numerical Analysis,我正在试验牛顿法计算平方根的不同实现。一个重要的决定是何时终止算法 显然,使用y*y和x之间的绝对差是不行的,其中y是x平方根的当前估计值,因为对于x的大值,可能无法以足够的精度表示其平方根 所以我应该使用相对标准。天真的我会用这样的东西: static int sqrt_good_enough(float x, float y) { return fabsf(y*y - x) / x < EPS; } static int sqrt足够好(float x,float y){ 返回f

我正在试验牛顿法计算平方根的不同实现。一个重要的决定是何时终止算法

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

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

static int sqrt_good_enough(float x, float y) {
  return fabsf(y*y - x) / x < EPS;
}
static int sqrt足够好(float x,float y){
返回fabsf(y*y-x)/x
这似乎效果很好。但最近我开始阅读Kernighan和Plauger的《编程风格的要素》,他们在第1章中为相同的算法提供了一个Fortran程序,其终止标准(翻译为C)为:

static int sqrt_good_enough(float x, float y) {
  return fabsf(x/y - y) < EPS * y;
}
static int sqrt足够好(float x,float y){
返回fabsf(x/y-y)

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

它们仍然是不等价的;底部的一个在数学上等价于fabsf(y*y-x)/(y*y)。我看到您的问题是,如果
y*y
溢出(可能是因为
x
FLT\u MAX
并且
y
被不幸选择),那么终止可能永远不会发生。下面的交互使用double

>>> 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
问题。规范化
x
以避免两个极端的复杂性。

这两个在数学上实际上并不完全相等,除非您为第一个极端编写fabsf(y*y-x)/(y*y) 但我认为关键是要使这里的表达式与牛顿迭代中计算y的公式相匹配。例如,如果y公式为y=(y+x/y)/2,则应使用Kernighan和Plauger的样式。如果是y=(y*y+x)/(2*y),则应使用(y*y-x)/(y*y) 通常,终止标准应为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++版本也有同样的问题。

对于一个以计算机上的数值计算为目标的beta StackExchange社区来说,这是一个很好的问题。溢出问题是我从未想到过的一个极好的问题。顺便说一下,注释(编辑1)现在移动到我的答案。