三分死循环 ZOJ Problem Set - 3203 Light Bulb 重看acm精度细节
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3203
我深知自己是菜鸟,所以刷题只刷简单的和自己感兴趣的,在浙江大学的oj上看到了一个电灯的题目,感觉像中学物理,于是兴奋的做了一下,题意很简单,求一个凸函数的极大值,套用三分模型将会很快出解,但是我在测试时第二组数据出现了无输出的情况,推知是程序内部出现了死循环。
经过主句调试,发现当left=14.9998999···702,right=14.9998999···762时在赋值语句mid=(left+right)/2的结果始终是right的值并一直不变,使得程序死循环。
后来找朋友帮忙,他也没看,就说为什么你用float不用double,double多好,精度高多了。
我也知道是这样,但是在写程序的时候有事考虑内存和实际使用精度的情况还是会不自觉的使用float。抱着试试的心态,我改了所有的float为double,于是结果对了。
然后我回头分析了用float错误原因,百度后知道float最多保存小数7位,所以虽然计算结果看起来有很多位,right和left并不相等,但是float把他们存起来的时候,只管七位,
也就是它记载的left和right是相等的,所以就会死循环。
按照这种分析,那么是否存在一组特殊的数据使得left与right的值也在double的存储位之外才不同而也出现死循环呢?
绝对不会!
为什么呢?
因为我们在进行比较的时候,加上了一个eps,而eps是在double的小数位之内的,也就是单凭eps小数值都要变化,所以不会出现死循环。
如此分析后,可知使用float型数据时,定义eps=1e-9真是废柴,float根本就存不了9为小数。
所以要想让下面的程序在float情况下不死循环可以改eps为1e-6或1e-5次方。但是这样的话精度降了十多位,结果有很大概率出错!
所以一个结论;有事没事别用float!
1 #include<cstdio> 2 #include<cmath> 3 using namespace std; 4 #define eps 1e-9 5 double H,h,d; 6 double calcu(double x) 7 { 8 return (h*d-H*x)/(d-x)+x; 9 } 10 double solve(double a,double b) 11 { 12 double sum; 13 double left,right,mid,mmid; 14 left=a; 15 right=b; 16 while(left+eps<=right) 17 { 18 mid=(left+right)/2; 19 mmid=(right+mid)/2; 20 sum=calcu(mid); 21 if(calcu(mmid)<=sum) 22 right=mmid; 23 else 24 left=mid; 25 } 26 return calcu(left); 27 } 28 int main() 29 { 30 31 int n; 32 scanf("%d",&n); 33 while(n--) 34 { 35 scanf("%lf%lf%lf",&H,&h,&d); 36 37 printf("%.3f\n",solve(0,d*h/H)); 38 } 39 return 0; 40 }
关于存储类型带来的acm解题中的奇怪现象,我在另一篇博客中有提到,有兴趣可以一看。
posted on 2013-08-06 21:30 plank george 阅读(338) 评论(0) 编辑 收藏 举报