题目描述:
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;
}
优化了很久,代码还是这么长,模拟题真的折磨人……
法二
也可将一个实数分成整数和小数两部分,对整数部分和小数部分的加法分别进行高精度模拟,注意可能要进位。