北大-计算概论-练习-求一元二次方程的根
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<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定得更小就行了。