一种可能性如下所示:
#include <math.h>
/* Iterations Accuracy
* 2 6.5 digits
* 3 20 digits
* 4 62 digits
* assuming a numeric type able to maintain that degree of accuracy in
* the individual operations.
*/
#define ITER 3
double dist(double P, double Q) {
/* A reasonably robust method of calculating `sqrt(P*P + Q*Q)'
*
* Transliterated from _More Programming Pearls, Confessions of a Coder_
* by Jon Bentley, pg. 156.
*/
double R;
int i;
P = fabs(P);
Q = fabs(Q);
if (P<Q) {
R = P;
P = Q;
Q = R;
}
/* The book has this as:
* if P = 0.0 return Q; # in AWK
* However, this makes no sense to me - we've just insured that P>=Q, so
* P==0 only if Q==0; OTOH, if Q==0, then distance == P...
*/
if ( Q == 0.0 )
return P;
for (i=0;i<ITER;i++) {
R = Q / P;
R = R * R;
R = R / (4.0 + R);
P = P + 2.0 * R * P;
Q = Q * R;
}
return P;
}
这仍然会在每次迭代中进行几次除法和四次乘法,但每次输入很少需要超过三次迭代(通常两次就足够了)。至少对于我见过的大多数处理器来说,这通常比 sqrt 本身要快。
目前它是为doubles 编写的,但假设您已经实现了基本操作,将其转换为使用定点应该不会很困难。
关于“相当稳健”的评论引发了一些疑问。至少正如最初所写的那样,这基本上是一种相当反常的说法,“它可能并不完美,但至少比直接实现勾股定理要好很多。”
特别是,当您对每个输入进行平方时,表示平方结果所需的位数大约是表示输入值所需位数的两倍。添加后(只需要一个额外的位),您取平方根,这使您回到需要与输入大致相同的位数。除非您的类型比输入的精度高得多,否则很容易产生非常糟糕的结果。
此算法不直接对任一输入求平方。中间结果仍然有可能下溢,但它的设计是这样的,当它这样做时,结果仍然会出现以及使用的格式支持。基本上,发生这种情况的情况是您有一个 非常 锐角三角形(例如,90 度、0.000001 度和 89.99999 度)。如果它足够接近 90、0、90,我们可能无法表示两条长边之间的差异,因此它会将斜边计算为与另一条长边的长度相同。
相比之下,当勾股定理失败时,结果通常是 NaN(即,什么都不告诉我们),或者,根据使用的浮点格式,很可能看起来像一个合理的答案,但实际上是大错特错。