出于练习原因,我用C编写了以下算法:
#include <stdio.h>
#include <stdlib.h>
int main(){
double x=0;
printf("Enter the number: ");
scanf("%lf", &x);
int i = 0;
double v = 0;
double n=0;
int grenze = 12;
double z = 10;
/*
for(i=1; i<(x/2+1); i++){
v=i;
if((v*v) <= x){
n = i;
}
}
v=n;
*/
for(i=1; i<grenze+1; i++){
z = z * 0.1;
while(v*v<x){
v = v + z;
if(v*v<x){
n = v;
}
}
v=n;
}
printf("%.10f\n", n);
}
这很有效,但是对于大于某个值的数字(我不知道何时开始),例如50.000.000.000,该程序会冻结。
有什么我在这里看不到的吗?
答案 0 :(得分:2)
对于足够大的数字v
和足够小的数字z
,v = v + z
;是无操作 - v
不会改变。这使你的循环运行了很长时间。
算法不是一个好选择 - 您应该查找Newton-Raphson方法。我不相信你的算法适用于像1E-70
这样的极端数字(并且你已经证明它对1E+70
之类的数字不起作用)。请注意,在具有IEEE浮点支持的大多数机器上,双精度范围最高可达1E±300或更高。
你能解释一下为什么某个限制
v = v + z
是一个无操作者吗?
浮点数具有有限的十进制数字 - 通常为大约16左右。如果您添加1E+16
和1E-16
,则结果为1E+16
;没有足够的有效数字来存储额外信息。您以5E+10
为起点;您生成分数z = 1.0
然后0.1
,0.01
,... 0.000 000 000 01
或其左右(朋友之间的数量级是多少?)。当您添加最小值时,最终不会更改任何内容。
有关该主题的规范性文件为What Every Computer Scientist Should Know About Floating-Point Arithmetic或来自ACM。
答案 1 :(得分:0)
如果你在while循环中写一个printf
语句来向你展示v
,你会看到它在某些时候停止变化(这很糟糕,因为你用它来退出你的循环! )。
我将printf("DEBUG: entered for loop, v=%lf\n", v);
放入while循环中并看到了:
DEBUG: entered for loop, v=173205.080757
DEBUG: entered for loop, v=173205.080757
DEBUG: entered for loop, v=173205.080757
DEBUG: entered for loop, v=173205.080757
DEBUG: entered for loop, v=173205.080757
DEBUG: entered for loop, v=173205.080757
DEBUG: entered for loop, v=173205.080757
DEBUG: entered for loop, v=173205.080757
您添加的z非常小,当v为173205时,它实际上并未更改该值。您可以通过将v
的类型更改为long double
来扩展范围。但是,因为我们只有一定数量的位可以使用,所以仍然有一个上限,在其中添加足够小的数字不会改变可表示的值。