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

北大-计算概论-练习-求一元二次方程的根

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:


【解决此问题的通法】

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

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

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

如果要求更高的精度,则把precision定得更小就行了。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值