读入两个字符串,字符串除了数字还可能包括 ‘—’、‘E’、‘e’、’.’,相加之后输出结果,如果是浮点型,要求用科学计数法表示(最多包含10个有效数字)。
输入包含多组测试数据。
每组输入占两行,每行一个字符串,测试数据保证字符串的构成严格按照题目中的描述。
输出两个数字相加的结果,每组输出占一行。
34.56
2.45e2
2.7956e2
这道题的描述不是很清楚,负号“-”是在哪里没有说明,1、负号可能出现在首端,表示这是一个负数;2、也可能出现在“e”的后面,表示10的负几次方。所以这道题我刚看的时候觉得有点棘手,一开始我个人是考虑2的情况的。
当时的思路是:
但是,当时通过这么一个思路遇到了一些问题,就是对于小数,要保留多少位来输出没法来确定,遂夭折,但此时我仍然在意着这个负号到底是属于哪种情况?
于是,在自己的思路不行的情况下我在网上看了看其他人的博客,感谢子夜葵分享的代码,代码在codeup上提交成功,但该博客内的代码没有注释,所以自己在搞明白之后对代码做了部分分析与注释,写出来跟大家分享一下。
#include
int main()
{
char str1[50],str2[50];
long long s,s1,s2,ans;
int i,a1,a2,a,b,c,w,flag;
while(scanf("%s %s",str1,str2)!=EOF){
s1=s2=flag=b=c=a1=0;
for(i=0;str1[i];i++){
if(str1[i]=='-')
flag=1;
else if(str1[i]=='.')
c=1;
else if(str1[i]=='e'||str1[i]=='E'){
sscanf(str1+i+1,"%d",&b);
a1+=b;
break;
}
else{
s1=s1*10+str1[i]-'0';
a1-=c;
}
}
if(flag) s1=-s1;
flag=b=c=a2=0;
for(i=0;str2[i];i++){
if(str2[i]=='-')
flag=1;
else if(str2[i]=='.')
c=1;
else if(str2[i]=='e'||str2[i]=='E'){
sscanf(str2+i+1,"%d",&b);
a2+=b;
break;
}
else{
s2=s2*10+str2[i]-'0';
a2-=c;
}
}
if(flag) s2=-s2;
if(a1<a2)
for(;a1<a2;a2--)
s2*=10;
else if(a1>a2)
for(;a1>a2;a1--)
s1*=10;
a=a1;s=s1+s2;
if(!s){
printf("0\n");
continue;
}
while(a<0&&s%10==0){
s/=10;
a++;
}
if(a>=0){
printf("%lld",s);
for(i=0;i<a;i++)
printf("0");
printf("\n");
continue;
}
flag=0;
if(s<0){
s=-s;
flag=1;
}
ans=1;w=0;
while(ans<=s){
ans*=10;
w++;
}
if(ans>1){
ans/=10;
w--;
}
if(flag)
printf("-");
printf("%lld",s/ans);
if(ans>1)
printf(".%lld",s%ans);
printf("e%d\n",a+w);
}
return 0;
}
根据对代码的分析,个人整理出来的思路是这样的:
首先,负号是我上述的第1种情况,即只用于表示正负数;
然后,将字符串转换为数值,这个地方跟我的思路不同,下面会详细说;
最后,相加,根据相加的结果来判断以什么样的方式输出。
char str1[50],str2[50];
long long s,s1,s2,ans;
int i,a1,a2,a,b,c,w,flag;
1、先定义了一堆变量,我们先不管。
for(i=0;str1[i];i++){
if(str1[i]=='-')//如果有负号
flag=1;//flag是判断该数是不是负数
else if(str1[i]=='.')//如果是小数
c=1;
else if(str1[i]=='e'||str1[i]=='E'){
sscanf(str1+i+1,"%d",&b);//如果有e,将e后面的数赋给b
a1+=b;//a1是e的指数
break;
}else{
s1=s1*10+str1[i]-'0';//s1存储这个数
a1-=c;
}
}
if(flag) s1=-s1;//如果e前有负号,s1反转,变为负数
2、第1个for循环,主要是什么意思呢?其实就是把字符串表示为数值型加科学计数法的方式。
这段代码的转换思路如下:
i从0开始到末尾,
如果第i个字符是‘-’,表明是负数,所以flag为1;
如果第i个字符是‘.’,令c为1(这里c=1,是用于下面转换为整型数值);
如果第i个字符是‘e’或‘E’,就把e后面的指数加到a1上去;
如果第i个字符是一个数,就令数不断该边,转换成数。
这里a1-=c的意思就是,一开始,c是0,那么当不遇到小数点的时候,这个整数在增大,a1也没变,当遇到小数点后,c变为1,没往后增加一位,就要让指数减1。
以 24.56e3 来举例:
这里就是把24.56e3转换为2456e1。
for(i=0;str2[i];i++){
if(str2[i]=='-')//如果有负号
flag=1;//flag是判断该数是不是负数
else if(str2[i]=='.')//如果是小数
c=1;
else if(str2[i]=='e'||str2[i]=='E'){
sscanf(str2+i+1,"%d",&b);//如果有e,将e后面的数赋给b
a2+=b;//a2是e的指数
break;
}else{
s2=s1*10+str2[i]-'0';//s2存储这个数
a2-=c;
}
}
if(flag) s2=-s2;//如果e前有负号,s2反转,变为负数
3、第2个for循环是对字符串str2的操作,与上面相同。
if(a1<a2){
for(;a1<a2;a2--){
s2*=10;//将10^a2-a1乘到s2上,转化为数值,来统一两个数的e的指数
}
}else if(a1>a2){
for(;a1>a2;a1--){
s1*=10;
}
}
a=a1;//a为相加后的e的指数
s=s1+s2;//数值部分相加
4、这部分就是统一两个数字的指数
比如一个是2456e1,一个是3452e2,就把第二个转换为34520e1。然后相加,将前面的数值赋给s,后面的指数赋给a。
if(!s){//若s为0
printf("0\n");
continue;
}
while(a<0&&s%10==0){//若数值类似于1000且后面有e^a次方,可以改一下
s/=10;
a++;
}
if(a>=0){//若a>0,则化为整数
printf("%lld",s);
for(i=0;i<a;i++){
printf("0");
}
printf("\n");
continue;
}
flag=0;
if(s<0){//s为负数
s=-s;
flag=1;
}
ans=1;
w=0;//w为s的位数
while(ans<=s){
ans*=10;//ans是对应位数的整十数,如1000
w++;
}
if(ans>1){//ans倒退一位,用于后边小数点后面的输出
ans/=10;
w--;
}
if(flag){//s<0
printf("-");
}
printf("%lld",s/ans);
if(ans>1){
printf(".%lld",s%ans);
}
printf("e%d\n",a+w);
5、这一部分就是输出:
1、如果s为0,说明结果为0,直接输出0;
2、然后将整型改一下,比如2500e2改为25e4;
3、如果a>0,说明是个整数,所以不用科学计数法,输出;
4、下面负数与整数输入方式一样,就是负数前面输一个’-'号,
以2456e1举例:
首先用w记录2456的位数,即w=3,
然后根据w给一个1000的数ans,
用2456/1000得到2,输出”2.“,
2456%1000=456,输出”456“,
再接着输出”e“和”a+w“。即输出"e4",
最终输出2.456e4。
因此整个代码的思路就是这样,跟我一开始的思路相比,
1、用整型和e来表示这个数,而我一开始是想直接用一个double类型的数来表示,这样就避免了我之前遇到的输出位数不确定是多少的情况。比如1.234567891,用%.lf就是默认保留6位小数。
2、通过分析这个代码我们知道这道题的负号是用于正负的,而非e的负多少次方,为此我验证过,当输入1和1.245e-2时,输出是1.1245e0,这显然是不对的。
目前就这么多,写得不怎么好,大家将就着看吧。