A+B(II)——用高精度方法模拟实数加法

题目描述:

Description

龙龙觉得之前的二进制加法可能对你来说太简单了,正好你也学完了“字符串处理”专题,那么就来考验一下你对大数加法的熟练程度吧?下面举一个实数加法运算的实例:

   2.01
+  1.0 
-------
   3.01

请你模拟这个过程。

Input

第一行输入一个实数a表示加数,第二行输入一个实数b表示被加数,保证输入实数的长度均小于1000,且a、b非负,可能有前导0和后导0出现。

Output

请模拟实数加法,按题目描述的格式输出正确的运算结果,注意换行,没有多余的空格和换行。

Hint

·前后导0:前导0举例0002.3,注意0.23的0不是前导0,后导0举例12.300

·输出格式:注意横杠字符-的长度为你每行输出字符串的总宽度,也就是说可能有前缀空格,也可能有后缀空格,输出的实数如果有小数点,则按小数点对齐,其余部分用空格补满,若加数与被加数小数部分精度不同,则答案请按精度更高的小数位输出,精度不足部分请补0,答案的整数部分请不要输出多余的前导零。

思路描述:

这题思路较简单,很显然是要用高精度加法模拟实数加法,需要采用a,b两个数组存储两个数字的每一位,相加得到c数组,之后只需要根据具体要求打印加法竖式。

难点在于整数和小数部分的处理。有的大佬说分开存储,不过我采用的方法是将小数部分和整数部分在一起存储,并对小数位少的数字在低位补0。这样处理的话我认为加法实现较简单,不过需要记录a、b两数字的小数位数,打印时要注意判断。

踩坑总结:

1.注意题目要求,只有数字c不用输出前导0,数字a、b的前导0均要输出

2.a、b两数可能有一个整数,这样的话打印时小数点的位置要用空格代替,如果两数均是整数,只需打印完整数便可跳出当前循环;

3.在去除c的前导0的过程中,要注意c的整数部分可能全部为0,因此我加了一个条件:当c[0]==dlen(c的整数部分全部去除完,dlen是a、b两数小数位数的较大值)时便停止去除前导0。

代码实现:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#define SIZE 10010  
char s1[SIZE],s2[SIZE];
int a[SIZE],b[SIZE],c[SIZE];
void str2num(char *s,int *ar,int dlen,int dlen1)
{
	for(int i=1;i<=dlen-dlen1;i++)/*补足小数位数*/
	 	ar[i]=0;
	if(dlen1) for(int i=1,flag=0;i<=ar[0]+1;i++)/*有小数*/
	{
		if(s[ar[0]-i+1]=='.') 
			{ flag=1; continue; }
		else if(!flag) 
			ar[i+dlen-dlen1]=s[ar[0]-i+1]-'0';
		else
			ar[i-1+dlen-dlen1]=s[ar[0]-i+1]-'0';
	}
	else for(int i=1;i<=ar[0];i++) /*整数*/
		ar[i+dlen-dlen1]=s[ar[0]-i]-'0';
	ar[0]+=dlen-dlen1;
}

int max(int a,int b,int c)
{
	int res=(a>b)?a:b;
	res=(res>c)?res:c;
	return res;
}

int main()   
{  
	gets(s1);
	gets(s2);
	a[0]=strlen(s1);
	b[0]=strlen(s2);
	
	int dlen1,dlen2;/*计算a,b小数部分长度*/
	char *p=strchr(s1,'.');
	if(p!=NULL)	{ a[0]--; dlen1=a[0]-(p-s1)/sizeof(char); }
	else  dlen1=0;
	char *q=strchr(s2,'.');
	if(q!=NULL) { b[0]--; dlen2=b[0]-(q-s2)/sizeof(char); }
	else  dlen2=0; 
	int dlen=dlen1>dlen2?dlen1:dlen2;
	
    str2num(s1,a,dlen,dlen1);
    str2num(s2,b,dlen,dlen2);
    
    c[0]=a[0]>b[0]?a[0]:b[0];
    for(int i=1;i<=c[0];i++)
    {
    	c[i]+=a[i]+b[i];
    	if(c[i]>=10)
    	{
    		c[i]-=10;
    		c[i+1]++;
		}
	}
	if(c[c[0]+1]!=0) c[0]++;
	while(c[c[0]]==0&&c[0]>dlen) c[0]--;/*去除答案前导0 && 判断答案整数部分是否全0*/
	
	int width=max(a[0],b[0],c[0])+4;
	for(int i=1;i<=4;i++)
	{
		if(i==1) for(int j=1;j<=width;j++)
		{
			if(j<=width-a[0]-1) printf(" ");
			else if(j<width-dlen) printf("%d",a[width-j]);
			else if(j==width-dlen) 
				if(dlen1!=0) printf(".");
				else if(dlen2!=0) printf(" ");
				else break; 
			else if(j<=width-dlen+dlen1) printf("%d",a[width-j+1]);
			else printf(" ");
		}
		
		else if(i==2) for(int j=1;j<=width;j++)
		{
			if(j==1) printf("+");
			else if(j<=width-b[0]-1) printf(" ");
			else if(j<width-dlen) printf("%d",b[width-j]);
			else if(j==width-dlen) 
				if(dlen2!=0) printf(".");
				else if(dlen1!=0) printf(" ");
				else break;
			else if(j<=width-dlen+dlen2) printf("%d",b[width-j+1]);
			else printf(" ");
		}
		
		else if(i==3) for(int j=1;j<=width;j++) 
		{
			if(j==width&&dlen1==0&&dlen2==0) break;
			printf("-");
		}
		
		else if(c[0]>dlen) for(int j=1,flag=0;j<=width;j++)/*答案整数部分非0*/
		{
			if(j<=width-c[0]-1) printf(" ");
			else if(j<width-dlen) 
				if(c[width-j]==0) 
					if(flag==0) printf(" ");/*flag用于判断前导0是否消除*/
					else printf("%d",c[width-j]);
				else { printf("%d",c[width-j]); flag=1; }
			else if(j==width-dlen) 
				if(dlen1!=0||dlen2!=0) printf(".");
				else break;
			else printf("%d",c[width-j+1]);
		}
			
		else for(int j=1;j<=width;j++)/*答案整部部分全0*/
		{
			if(j<=width-dlen-2) printf(" ");
			else if(j==width-dlen-1) printf("0");
			else if(j==width-dlen)
				if(dlen>0) printf(".");/*有小数部分*/
				else break;/*整数0*/
			else printf("%d",c[width-j+1]);
		}  
		printf("\n");
	}
    return 0;  
}  

优化了很久,代码还是这么长,模拟题真的折磨人……

法二

       也可将一个实数分成整数和小数两部分,对整数部分和小数部分的加法分别进行高精度模拟,注意可能要进位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值