我已经实现了这个功能:
double heron(double a)
{
double x = (a + 1) / 2;
while (x * x - a > 0.000001) {
x = 0.5 * (x + a / x);
}
return x;
}
Run Code Online (Sandbox Code Playgroud)
该功能按预期工作,但是我希望对其进行改进。它应该使用不间断的while循环来检查是否类似于x * xis a。a是用户应输入的数字。
到目前为止,我没有使用该方法的工作功能...这是我惨败的尝试:
double heron(double a)
{
double x = (a + 1) / 2;
while (x * x != a) {
x = 0.5 * (x + a / x);
}
return x;
}
Run Code Online (Sandbox Code Playgroud)
这是我的第一篇文章,因此,如果有任何不清楚或需要补充的内容,请告诉我。
尝试失败次数2:
double heron(double a)
{
double x = (a + 1) / 2;
while (1) {
if (x * x == a){
break;
} else {
x = 0.5 * (x + a / x);
}
}
return x;
}
Run Code Online (Sandbox Code Playgroud)
它应该利用和无休止的
while循环,以检查是否类似的东西x * x就是a
问题:
收敛缓慢
当首字母x很错误时,改进后的|x - sqrt(a)|误差可能仍然只有一半。鉴于的范围很广double,可能需要数百次迭代才能接近。
参考:苍鹭的公式。
对于新颖的第一估计方法:快速反平方根。
溢出
x * x在x * x != a容易出现溢出。 x != a/x提供了没有该范围问题的类似测试。如果发生溢出,x可能会被“无限”或“非数字”“感染”而无法实现收敛。
振荡
一旦x“接近” sqrt(a)(在2倍之内),误差收敛将是二次的-“正确”的位数使每次迭代加倍。这种情况一直持续到,x == a/x或者由于double数学的特殊性,商x将在两个值之间无休止地振荡。
进入这种振荡会导致OP的环路无法终止
结合使用测试工具,证明了足够的收敛性。
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
double rand_finite_double(void) {
union {
double d;
unsigned char uc[sizeof(double)];
} u;
do {
for (unsigned i = 0; i < sizeof u.uc; i++) {
u.uc[i] = (unsigned char) rand();
}
} while (!isfinite(u.d));
return u.d;
}
double sqrt_heron(double a) {
double x = (a + 1) / 2;
double x_previous = -1.0;
for (int i = 0; i < 1000; i++) {
double quotient = a / x;
if (x == quotient || x == x_previous) {
if (x == quotient) {
return x;
}
return ((x + x_previous) / 2);
}
x_previous = x;
x = 0.5 * (x + quotient);
}
// As this code is (should) never be reached, the `for(i)`
// loop "safety" net code is not needed.
assert(0);
}
double test_heron(double xx) {
double x0 = sqrt(xx);
double x1 = sqrt_heron(xx);
if (x0 != x1) {
double delta = fabs(x1 - x0);
double err = delta / x0;
static double emax = 0.0;
if (err > emax) {
emax = err;
printf(" %-24.17e %-24.17e %-24.17e %-24.17e\n", xx, x0, x1, err);
fflush(stdout);
}
}
return 0;
}
int main(void) {
for (int i = 0; i < 100000000; i++) {
test_heron(fabs(rand_finite_double()));
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
改进之处
sqrt_heron(0.0) 作品。
更改代码以获得更好的初始猜测。
double sqrt_heron(double a) {
if (a > 0.0 && a <= DBL_MAX) {
// Better initial guess - halve the exponent of `a`
// Could possible use bit inspection if `double` format known.
int expo;
double significand = frexp(a, &expo);
double x = ldexp(significand, expo / 2);
double x_previous = -1.0;
for (int i = 0; i < 8; i++) { // Notice limit moved from 1000 down to < 10
double quotient = a / x;
if (x == quotient) {
return x;
}
if (x == x_previous) {
return (0.5 * (x + x_previous));
}
x_previous = x;
x = 0.5 * (x + quotient);
}
assert(0);
}
if (a >= 0.0) return a;
assert(0); // invalid argument.
}
Run Code Online (Sandbox Code Playgroud)