R S*_*ahu 1 c algorithm newtons-method
我有以下函数用于使用Newton-Raphson方法计算数字的平方根.
double get_dx(double y, double x)
{
return (x*x - y)/(2*x);
}
double my_sqrt(double y, double initial)
{
double tolerance = 1.0E-6;
double x = initial;
double dx;
int iteration_count = 0;
while ( iteration_count < 100 &&
fabs(dx = get_dx(y, x)) > tolerance )
{
x -= dx;
++iteration_count;
if ( iteration_count > 90 )
{
printf("Iteration number: %d, dx: %lf\n", iteration_count, dx);
}
}
if ( iteration_count < 100 )
{
printf("Got the result to converge in %d iterations.\n", iteration_count);
}
else
{
printf("Could not get the result to converge.\n");
}
return x;
}
Run Code Online (Sandbox Code Playgroud)
他们适用于大多数人.然而,当他们不收敛y是1.0E21和1.0E23.可能还有其他数字,我还不知道,其功能不会收敛.
我测试了初始值:
y*0.5 - 开始使用的东西.1.0E10 - 最终结果附近的数字.sqrt(y)+1.0 - 显然,最终答案的数字非常接近.在所有情况下,函数都无法收敛1.0E21和1.0E23.我尝试了更低的数字,1.0E19以及更高的数字1.0E25.它们都不是问题.
这是一个完整的程序:
#include <stdio.h>
#include <math.h>
double get_dx(double y, double x)
{
return (x*x - y)/(2*x);
}
double my_sqrt(double y, double initial)
{
double tolerance = 1.0E-6;
double x = initial;
double dx;
int iteration_count = 0;
while ( iteration_count < 100 &&
fabs(dx = get_dx(y, x)) > tolerance )
{
x -= dx;
++iteration_count;
if ( iteration_count > 90 )
{
printf("Iteration number: %d, dx: %lf\n", iteration_count, dx);
}
}
if ( iteration_count < 100 )
{
printf("Got the result to converge in %d iterations.\n", iteration_count);
}
else
{
printf("Could not get the result to converge.\n");
}
return x;
}
void test(double y)
{
double ans = my_sqrt(y, 0.5*y);
printf("sqrt of %lg: %lg\n\n", y, ans);
ans = my_sqrt(y, 1.0E10);
printf("sqrt of %lg: %lg\n\n", y, ans);
ans = my_sqrt(y, sqrt(y) + 1.0);
printf("sqrt of %lg: %lg\n\n", y, ans);
}
int main()
{
test(1.0E21);
test(1.0E23);
test(1.0E19);
test(1.0E25);
}
Run Code Online (Sandbox Code Playgroud)
这是输出(基于64位Linux,使用gcc 4.8.4):
Iteration number: 91, dx: -0.000002
Iteration number: 92, dx: 0.000002
Iteration number: 93, dx: -0.000002
Iteration number: 94, dx: 0.000002
Iteration number: 95, dx: -0.000002
Iteration number: 96, dx: 0.000002
Iteration number: 97, dx: -0.000002
Iteration number: 98, dx: 0.000002
Iteration number: 99, dx: -0.000002
Iteration number: 100, dx: 0.000002
Could not get the result to converge.
sqrt of 1e+21: 3.16228e+10
Iteration number: 91, dx: 0.000002
Iteration number: 92, dx: -0.000002
Iteration number: 93, dx: 0.000002
Iteration number: 94, dx: -0.000002
Iteration number: 95, dx: 0.000002
Iteration number: 96, dx: -0.000002
Iteration number: 97, dx: 0.000002
Iteration number: 98, dx: -0.000002
Iteration number: 99, dx: 0.000002
Iteration number: 100, dx: -0.000002
Could not get the result to converge.
sqrt of 1e+21: 3.16228e+10
Iteration number: 91, dx: 0.000002
Iteration number: 92, dx: -0.000002
Iteration number: 93, dx: 0.000002
Iteration number: 94, dx: -0.000002
Iteration number: 95, dx: 0.000002
Iteration number: 96, dx: -0.000002
Iteration number: 97, dx: 0.000002
Iteration number: 98, dx: -0.000002
Iteration number: 99, dx: 0.000002
Iteration number: 100, dx: -0.000002
Could not get the result to converge.
sqrt of 1e+21: 3.16228e+10
Iteration number: 91, dx: 0.000027
Iteration number: 92, dx: 0.000027
Iteration number: 93, dx: 0.000027
Iteration number: 94, dx: 0.000027
Iteration number: 95, dx: 0.000027
Iteration number: 96, dx: 0.000027
Iteration number: 97, dx: 0.000027
Iteration number: 98, dx: 0.000027
Iteration number: 99, dx: 0.000027
Iteration number: 100, dx: 0.000027
Could not get the result to converge.
sqrt of 1e+23: 3.16228e+11
Iteration number: 91, dx: -0.000027
Iteration number: 92, dx: -0.000027
Iteration number: 93, dx: -0.000027
Iteration number: 94, dx: -0.000027
Iteration number: 95, dx: -0.000027
Iteration number: 96, dx: -0.000027
Iteration number: 97, dx: -0.000027
Iteration number: 98, dx: -0.000027
Iteration number: 99, dx: -0.000027
Iteration number: 100, dx: -0.000027
Could not get the result to converge.
sqrt of 1e+23: 3.16228e+11
Iteration number: 91, dx: 0.000027
Iteration number: 92, dx: 0.000027
Iteration number: 93, dx: 0.000027
Iteration number: 94, dx: 0.000027
Iteration number: 95, dx: 0.000027
Iteration number: 96, dx: 0.000027
Iteration number: 97, dx: 0.000027
Iteration number: 98, dx: 0.000027
Iteration number: 99, dx: 0.000027
Iteration number: 100, dx: 0.000027
Could not get the result to converge.
sqrt of 1e+23: 3.16228e+11
Got the result to converge in 35 iterations.
sqrt of 1e+19: 3.16228e+09
Got the result to converge in 6 iterations.
sqrt of 1e+19: 3.16228e+09
Got the result to converge in 1 iterations.
sqrt of 1e+19: 3.16228e+09
Got the result to converge in 45 iterations.
sqrt of 1e+25: 3.16228e+12
Got the result to converge in 13 iterations.
sqrt of 1e+25: 3.16228e+12
Got the result to converge in 1 iterations.
sqrt of 1e+25: 3.16228e+12
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释为什么上面的功能不收敛的1.0E21和1.0E23?
这个答案特别解释了为什么算法无法收敛1e23的输入.
您面临的问题被称为"大数字的小差异".特别是,你计算(x*x - y)/(2*x)哪里y是1e23和x大约3.16e11.
1e23 的IEEE-754编码是0x44b52d02c7e14af6.因此指数为0x44b,即十进制1099.由于指数被编码为偏移二进制,因此必须减少1023.然后你必须减去另外52来找到LSB的权重.
0x44b ==> 1099 - 1023 - 52 = 24 ==> 1 LSB = 2^24
Run Code Online (Sandbox Code Playgroud)
因此单个LSB的值为16777216.
此代码的结果
double y = 1e23;
double x = sqrt(y);
double dx = x*x - y;
printf( "%lf\n", dx );
Run Code Online (Sandbox Code Playgroud)
实际上是-16777216.
其结果是,当你计算x*x - y,其结果要么将是零,或者这将是16777216的倍数除以16777216通过2*x这2*3.16e11给出了一个0.000027错误,这是不是你的公差范围内.
两个最接近的值sqrt(y)是
0x4252682931c035a0
0x4252682931c035a1
Run Code Online (Sandbox Code Playgroud)
当你对那些数字进行平方时
0x44b52d02c7e14af5
0x44b52d02c7e14af7
Run Code Online (Sandbox Code Playgroud)
所以两者都不匹配所需的结果,即
0x44b52d02c7e14af6
Run Code Online (Sandbox Code Playgroud)
因此算法永远不会收敛.
算法适用于1e25的原因是运气.1e25的编码是
0x45208b2a2c280291
Run Code Online (Sandbox Code Playgroud)
编码sqrt(1e25)是
0x428702337e304309
Run Code Online (Sandbox Code Playgroud)
当你对那个数字进行平方时,你会得到
0x45208b2a2c280291
Run Code Online (Sandbox Code Playgroud)
因此x*x - y正好是0,算法很幸运.