算法竞赛入门经典(第2版)习题3-12 浮点数 Floating-Point Numbers UVa11809

博主在解决UVa11809浮点数问题时遇到了挑战,经过多次尝试和分析,发现错误源自浮点数转整数的系统误差。通过建立换算公式组、尾数和阶码表,最终成功AC。关键在于正确处理浮点数转换,避免类似1.0000000*10^9变成999999999的误差。

这题有点逼近我的智商上限了,看了好几遍才明白大概意思。编程的时候脑子里一直有点乱,卡了我将近一周,代码整体重写了三次。每次都可以实现样例,但是一直WA。

本来都打算跳过去了,但总是有点不甘心。

决定尝试最后一次:把所有可能情况的输入数据都算出来当做测试数据,最终发现WA是由于浮点数转换成整数时的系统误差导致的。

印象里书中样例曾经在浮点数转换整数时+0.5,我这里没有这样做

结果在某个测试样例中double 1.0000000*1000000000转换为整数的结果成了999999999,最终导致WA。

修改了这个bug后,顺利ac。

思路如下:

1.利用10^1=1.25*2^3逐级推算出1<=n<=10时10^n与2^m的换算公式组

2.推算出0<=M<=9各值时所有的十进制小数尾数,得到M表

3.推算出1<=E<=30各值时所有的2的十进制阶数,得到E表

4.读入s串并分割,把输入尾数存在double ap中,阶码存在int B中

5.利用第一步中求出的公式把10^int B转换为中转十进制浮点数double zz*(2^int E)其中1<=zz<2

6.计算出中转十进制浮点数zz* ap 结果存入 double apzz,其中0.5<=apzz<1,并修正E值

7.用apzz在M表中查询,得到M值

8.用E在E表中查询,得到E值


//#define LOCAL
//#define TESTING
#include<stdio.h>
#include<math.h>
#include<string.h>
#define MAXN 1000

char s[25];
long DM[10];

int main()
{
	#ifdef LOCAL
		freopen("xt3-12.in","r",stdin);
		freopen("xt3-12-1.out","w",stdout);
	#endif
	//计算10^(10^n)=double zw[]*2^long long zj[]公式组 
	#ifdef TESTING
	printf("计算10^(10^n)=double zw[]*2^long long zj[]公式组\n");	
	#endif	
	long long zj[10];
	double zw[10] ;
	int tempz=1;
	memset(zj,0,sizeof(zj));
	memset(zw,0,sizeof(zw));	
	zj[0]=3;
	zw[0]=1.25;
	for(int i = 1; i<=9; i++)
	{
		zj[i] = 10*zj[i-1];
		zw[i] = pow(zw[i-1],10);
		for(int j = 0; j < 10; j++)
		{
			if(zw[i]>2)
			{
				zw[i]/=2;
				zj[i]++;
			}
			else break;
		}
		tempz *= 10;	
	}
	for(int i = 1; i<=9; i++)
	{
		#ifdef TESTING		
		printf("10^(10^%d)=zw[%d]%.15lf*2^zj[%d]%lld\n", i,i,zw[i],i, zj[i]);
		#endif		
	}
	//计算结束 
	
	//求M表	
	#ifdef TESTING
	printf("可能的十进制m值\n");	
	#endif		
	double m[15];
	memset(m,0,sizeof(m));
	for(int i = 0; i <= 9; i++)
	{			
		m[i]=(pow(2,i+1)-1.0)/pow(2,i+1);	
		#ifdef TESTING
		printf("m[%d]=%.15lf\n", i, m[i]);
		#endif		
	}
	//求M表结束	
	//求e表
	long long e[35];
	e[0]=0;
	e[1]=1;
	for(int i = 2; i<=30	;i++)
	{
		e[i]=e[i-1]+pow(2,i-1);
	}
	#ifdef TESTING
	printf("e表,2^E\n");	
	for(int i = 0; i<=30;i++) printf("2^E[%d]=2^%ld\n", i, e[i]);
	#endif	
	//求e表结束		
	#ifdef TESTING	
		int T = 1;
	#endif
	while(scanf("%s",s)!=EOF)
	{
		int A,a[15],C,c[15],len;
		long long E,B=0;
		double ap = 0,zz;		
		memset(a,0,sizeof(a));
		memset(c,0,sizeof(c));
		//将s串分割,把尾数存在double ap中,阶码存在int B中		
		len = strlen(s);
		if((s[0]=='0')&&(s[1]=='e')&&(s[2]=='0')) break;
		A = s[0]-'0';
		for(int i = 0; i<15; i++) 
		{
			a[i] = s[16-i]-'0';
			ap = (ap+a[i])/10.0;			
		}
		ap += A; 
		for(int i = 0; i<(len-18); i++)
		{
			B *= 10;
			B += s[i+18]-'0';
		}		
		//分割保存结束 
		
		//把10^B转换为中转十进制浮点数D:zz*(2^E)
		#ifdef TESTING
		printf("\nCase:%d\n把10^B转换为中转十进制浮点数D:zz*(2^E)\n",T++);	
		#endif	
		E = 0;
		zz = 1.0;
		int tempB,ij = 0; 
		tempB = B;
		while(tempB!=0)
		{
			
			E+=zj[ij]*(tempB%10);
			zz *= pow(zw[ij],tempB%10);	
			while(zz>2)
			{
				zz /= 2;
				E++;
			}			
			#ifdef TESTING
			printf("E=%lld zz=%.15lf tempB=%d tempB%10=%d\n", E, zz,tempB,tempB%10);
			#endif			
			tempB=tempB/10;				
			ij++;	
		}
		#ifdef TESTING
		printf("E=%lld zz=%.15lf B=%lld\n", E, zz,B);
		#endif				
		//转换结束
		
		//计算 中转十进制浮点数D:zz* ap 结果存入 0.double apzz
		//(intC.int c[ ]*(2^E)=A.a[]*(10^B))		
		double zz2,apzz;		
		if(zz>0)
		{
			apzz = ap * zz; 
			int tempapzz=apzz*10000000+0.5;
			while(tempapzz>=10000000)
			{
				apzz /= 2;
				tempapzz /=2;
				E++;
				#ifdef TESTING							
				printf("tempapzz=%d apzz=%.15lf\n", tempapzz,apzz);				
				printf("\n");		
				#endif				
			}			
		}		
	#ifdef TESTING			
		printf("apzz:%.15lf E:%lld\n", apzz,E); 		
		printf("len=%d s=%s\n", len, s); 		
		printf("s=%.15lfe%lld\n", ap,B);				
		printf("\n");		
	#endif		
		//计算结束 		
	
		//根据apzz查M表 确认M值 输出 
		long long tempm,M;
		tempm = apzz*10000000;
		for(int i = 0; i < 10; i++)
		{
			#ifdef TESTING
			printf("tempm=%lld m[%d]=%.15lf\n",tempm,i,m[i]);
			#endif
			int tempmi = m[i]*10000000;	
			if(tempm == tempmi)
			{
				M = i;			
				break;				
			}
		}
		printf("%d ", M);
		//根据apzz查M表 确认M值 输出 结束 	
		
		//根据E查e表 确认e值 输出 
		for(int i = 0; i <= 30; i++)
		{			
			if(E==e[i]) 
			{				
				printf("%d\n",i);
				break;
			}			
		}
		//根据E查e表 确认e值 输出 结束 
	} 
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值