【问题标题】:Floating point square root algorithms浮点平方根算法
【发布时间】:2013-07-01 17:36:55
【问题描述】:

我想知道是否有人可以提供一些可以利用硬件除法器的浮点平方根算法的示例。

额外细节: 我正在开发一个浮点单元,它有一个硬件浮点 IEEE-754 32 位乘法器、加法器和除法器。我已经使用 Newton-Raphson 方法实现了平方根,仅使用乘法和加法/减法,但现在我想比较平方根的吞吐量,如果我有可用的硬件除法器。

1 个难以准确计算的特定输入是 0x7F7FFFFF (3.4028234663852886E38) 的平方根。

【问题讨论】:

  • 直接牛顿求 sqrt(r) 的方法是s <- (s + r/s)/2。您需要小心舍入以使其正确。
  • @tmyklebu,该方法需要正确的初始猜测,而且我相信你写的公式不正确en.wikipedia.org/wiki/…
  • @Lior Kogan:您链接的文章存在缺陷,造成的损害比它所告知的要大。维基百科有一个很棒的页面专门介绍平方根算法:en.wikipedia.org/wiki/Methods_of_computing_square_roots

标签: c++ algorithm math floating-point computer-science


【解决方案1】:

@tmyklebu 提供的解决方案显然符合您的要求。

r = input value
s(0) = initial estimate of sqrt(r).  Example: r with its exponent halved.
s(n) = sqrt(r)

s

它具有二次收敛性,执行请求的除法。对于 32 位浮点数,N = 3 或 4 应该这样做。

[编辑 N = 2 为 32 位浮点数,N = 3(可能为 4)为双精度]


[根据 OP 请求编辑] [编辑每个 OP 请求添加的 cmets]

// Initial estimate
static double S0(double R) {
  double OneOverRoot2 = 0.70710678118654752440084436210485;
  double Root2        = 1.4142135623730950488016887242097;
  int Expo;
  // Break R into mantissa and exponent parts.
  double Mantissa = frexp(R, &Expo);
  int j;
  printf("S0 %le %d %le\n", Mantissa, Expo, frexp(sqrt(R), &j));
  // If exponent is odd ...
  if (Expo & 1) {
    // Pretend the mantissa [0.5 ... 1.0) is multiplied by 2 as Expo is odd, 
    //   so it now has the value [1.0 ... 2.0)
    // Estimate the sqrt(mantissa) as [1.0 ... sqrt(2))
    // IOW: linearly map (0.5 ... 1.0) to (1.0 ... sqrt(2))
    Mantissa = (Root2 - 1.0)/(1.0 - 0.5)*(Mantissa - 0.5) + 1.0;
  }
  else {
    // The mantissa is in range [0.5 ... 1.0)
    // Estimate the sqrt(mantissa) as [1/sqrt(2) ... 1.0)
    // IOW: linearly map (0.5 ... 1.0) to (1/sqrt(2) ... 1.0)
    Mantissa = (1.0 - OneOverRoot2)/(1.0 - 0.5)*(Mantissa - 0.5) + OneOverRoot2;
  }
  // Form initial estimate by using the above mantissa estimate and exponent/2
  return ldexp(Mantissa, Expo/2);
}

// S = (S + R/S)/2 method
double Sqrt(double R) {
  double S = S0(R);
  int i = 5;  // May be reduced to 3 or 4 for double and 2 for float
  do {
    printf("S  %u %le %le\n", 5-i, S, (S-sqrt(R))/sqrt(R));
    S = (S + R/S)/2;
  } while (--i);
  return S;
}

void STest(double x) {
  printf("T  %le %le %le\n", x, Sqrt(x), sqrt(x));
}

int main(void) {
  STest(612000000000.0);
  return 0;
}

3 次迭代后收敛。

S0 5.566108e-01 40 7.460635e-01
S 0 7.762279e+05 -7.767318e-03
S 1 7.823281e+05 3.040175e-05
S 2 7.823043e+05 4.621193e-10
S 3 7.823043e+05 0.000000e+00
S 4 7.823043e+05 0.000000e+00
T 6.120000e+11 7.823043e+05 7.823043e+05

【讨论】:

  • 有没有更好的方法来开始猜测 s(0)。此外,无限迭代似乎仍然存在很大的错误
  • 大错误问题:每一步之后的误差大约是上一步的平方。例如1% -> 0.01% -> 0.000001% 等等。如果你没有看到这个,我会建议一个编码问题。也许先在另一个场所(例如 Excel)尝试该算法。我将编辑帖子以讨论初步估算
  • 我对 s <- (s + r/s)/2 的使用快速收敛了 612000000000。(3 次迭代 - 见帖子。)
  • 感谢您花时间输入。你如何衡量误差?对于一定数量的迭代,您能否始终保证完整的 ieee-754 32 位精度?
  • 错误=(功能输出-理想)/理想。是的,经过几次迭代可以保证完全(或完全 - 1)精度。最后一个精度位或 2 对您如何编码 s worst 的位置。 GTG
猜你喜欢
  • 1970-01-01
  • 2012-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-16
  • 1970-01-01
  • 2022-11-14
  • 2023-03-28
相关资源
最近更新 更多