您的位置:首页 > 其它

求一元二次方程的根:浮点数中的“0”

2014-03-29 17:23 253 查看
北大-计算概论-练习-求一元二次方程的根
http://ica.openjudge.cn/base1/4/
【坑爹】

这道题目我提交了八次,终于AC



貌似还有提交18次才AC的



【坑在哪里】

1、浮点数无法和0比较。解决办法是看fabs(x-0)与1e-5的关系,即与0的差值和一个小量比大小

2、当一个数如果在(-0.000005,0)之间,输出精确到小数点后5位,就是-0.0而不是期望的0。这也需要判断一下。

【解决办法】

double comp(double x)
{
if(x>-1e-5&&x<1e-5)
return 0;
else
return x;
}


【AC代码】

#include<stdio.h>
#include&l
4000
t;math.h>

double comp(double x) { if(x>-1e-5&&x<1e-5) return 0; else return x; }
main()
{
double a,b,c;
double x1,x2;
double delta;
double m,n;
int repeat,i;

scanf("%d",&repeat);
for(i=0;i<repeat;i++)
{
scanf("%lf%lf%lf",&a,&b,&c);
delta=b*b-a*c*4;
if(fabs(delta)<1e-5)
{
x1=(-b+sqrt(delta))/2/a;
printf("x1=x2=%.5lf\n",x1);
}
else if(delta>1e-5)
{
x1=(-b+sqrt(delta))/2/a;
x2=(-b-+sqrt(delta))/2/a;
if(a>1e-5)
printf("x1=%.5lf;x2=%.5lf\n",comp(x1),comp(x2));
else
printf("x1=%.5lf;x2=%.5lf\n",comp(x2),comp(x1));
}
else
{
m=-b/2/a;
n=sqrt(-delta)/2/a;
if(a>1e-5)
printf("x1=%.5lf+%.5lfi;x2=%.5lf-%.5lfi\n",comp(m),comp(n),comp(m),comp(n));
else
printf("x1=%.5lf+%.5lfi;x2=%.5lf-%.5lfi\n",comp(m),comp(-n),comp(m),comp(-n));
}
}
}


【问题的本质:浮点数无法存储真正的“0”】

其实我们了解下计算机中是怎样存储浮点数的,这个问题的答案就很明了了。 

解释一:

IEEE754标准中

    单精度浮点数(4byte)表示法:1bit符号位(S),8bit指数位(E,用阶码表示),23bit小数部分(尾数M)。

    双精度浮点数(8byte)表示法:1bit符号位,11bit指数位(用阶码表示),52bit小数部分(尾数)。

    所以一个规格化的单精度浮点数x的真值为x=((-1)^S)*(1.M)*(2^(E-127))

显然,x永远也不可能为绝对0。 针对上面的描述,当阶码E为全0且尾数M也全0时,可以认为表示的真值x为计算机中的绝对0值,再结合符号位S,有正0和负0之分;

解释二:见http://learn.akae.cn/media/ch14s04.html

      每个浮点数的表示都不唯一,例如17=(0.10001)2×25=(0.010001)2×26,这样给计算机处理增加了复杂性。为了解决这个问题,我们规定尾数部分的最高位必须是1,也就是说尾数必须以0.1开头,对指数做相应的调整,这称为正规化(Normalize)。由于尾数部分的最高位必须是1,这个1就不必保存了,可以节省出一位来用于提高精度,我们说最高位的1是隐含的(Implied)。这样17就只有一种表示方法了,指数部分应该是16+5=21=(10101)2,尾数部分去掉最高位的1是0001:

【解决此问题的通法】

利用差值的绝对值的精度来判断。

具体就是:f1和f2是两个浮点数,precision是我们自己设置的精度,比如1e-6。

则可以用 fabs(f1-f2)<=precision 来判断f1和f2是否相等。

如果要求更高的精度,则把precision定得更小就行了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: