C语言编程问题求解答

题目描述
求2个浮点数相加的和

题目中输入输出中出现浮点数都有如下的形式:
P1P2...Pi.Q1Q2...Qj
对于整数部分,P1P2...Pi是一个非负整数
对于小数部分,Qj不等于0

关于输入
第1行是测试数据的组数n,每组测试数据占2行,分别是两个加数。每组测试数据之间有一个空行,每行数据不超过100个字符

关于输出
n行,每组测试数据有一行输出是相应的和。输出保证一定是一个小数部分不为0的浮点数

例子输入
2
0.111111111111111111111111111111
0.111111111111111111111111111111

10000000.655555555555555555555555555555
1.444444444444444444444444444445
例子输出
0.222222222222222222222222222222
10000002.1

C语言版本的

#include<stdio.h>
#include<string.h>

typedef struct _Ans
{
    int ans1[150];
    int count1;
    int p10;
    int ans2[150];
    int count2;
    int p20;
}Ans;


int main(){
    int n;
    int i,j,k,t = 0, m = 0, l = 0;
    int alen,blen;
    char a[150],b[150];
    int a1[150]={0},b1[150]={0},a2[150]={0},b2[150]={0},ans1[150]={0},ans2[150]={0};
    scanf("%d",&n);
    
    if(n <= 0){
    return 0;
    }

    l = n;
    m = 0;
    Ans *ans = (Ans *)malloc(sizeof(Ans) * n);
    
    while(n--){
        scanf("%s",&a);
        scanf("%s",&b);
        alen=strlen(a);
        blen=strlen(b);
        for(i=0;i<150;i++){
            a1[i]=0;
            a2[i]=0;
            b1[i]=0;
            b2[i]=0;
            ans1[i]=0;
            ans2[i]=0;
            ans[m].ans1[i]=0;
            ans[m].ans2[i]=0;
        }

        ans[m].count1 = 0;
        ans[m].count2 = 0;

        ans[m].p10 = 0;
        ans[m].p20 = 0;
 
        for(i=0;i<alen;i++){
            if(a[i]=='.'){
                k=0;
                for(j=i-1;j>=0;j--){
                    a1[k++]=a[j]-'0';
                }
                k=0;
                for(j=i+1;j<alen;j++){
                    a2[++k]=a[j]-'0';
                }
            }
        }
        for(i=0;i<blen;i++){
            if(b[i]=='.'){
                k=0;
                for(j=i-1;j>=0;j--){
                    b1[k++]=b[j]-'0';
                }
                k=0;
                for(j=i+1;j<blen;j++){
                    b2[++k]=b[j]-'0';
                }
            }
        }
        for(i=140;i>0;i--){
            ans1[i]+=a2[i]+b2[i];
            if(ans1[i]>=10){
                ans1[i-1]+=1;
                ans1[i]-=10;;
            }
        }
        if (ans1[0]!=0) a1[0]=a1[0]+1;
 
        for(i=0;i<=140;i++){
            ans2[i]+=a1[i]+b1[i];
            if(ans2[i]>=10){
                ans2[i]%=10;
                ans2[i+1]+=1;
            }
        }

        for(i=140;i>=0;i--){
            if(ans2[i]!=0){
                for(j=i;j>=0;j--){
                    ans[m].ans2[ans[m].count2++] = ans2[j];
                }
                break;
            }
        }
        if(i==-1) {
            ans[m].p20 = 1;
        }
        for(i=140;i>0;i--){
            if(ans1[i]!=0){
                for(j=1;j<=i;j++){
                    ans[m].ans1[ans[m].count1++] = ans1[j];
                }
                break;
            }
        }
        if(i==0) 
        {
            ans[m].p10 = 1;
        }
        m ++;
        printf("\n");
    }

    printf("\n");

    for (i = 0; i < l; i++)
    {
        for (j = 0; j < ans[i].count2; j++)
        {
            printf("%d",ans[i].ans2[j]);
        }

        if(ans[i].p20==1) 
        {
            printf("0");
        }
        printf(".");
        for (j = 0; j < ans[i].count1; j++)
        {
            printf("%d",ans[i].ans1[j]);
        }

        if(ans[i].p10==1) 
        {
            printf("0");
        }
        printf("\n");
    }

    free(ans);

    return 0;
}

运行结果:

img

好问题,收藏了

你这实际上就属于大整数的加减法类问题,只是把大整数变成了浮点数,其它没有区别,重点还是考虑如何存放很长的数并定义计算逻辑


一:大整数存储原理

用链栈的方式,每输入一个数字就压栈,同时用一个常量step记录数字长度放在栈顶方便后续使用,以“#”结束,分别将两个操作数压入S1和S2,结果放入S栈,完美实现任意长度的数字存储
(毕竟输入时是从左到右输入,计算时是从右到左计算,输出时又是从左往右,与栈的特点相符,经过尝试,加减运算全程确实完全符合这一规律)
二:加法
从数字个位开始即从栈顶开始,每次从S1和S2各弹出一个数,用k记录是否进位(01),进行一位数加法运算(S1+S2+k),将(S1+S2+k)%10压入S栈,循环执行下去,若其中一个栈已空,即直接把另一个栈加上进位k压入结果S栈,直到两个栈都为空。

三:减法
方法和加法类似,进位k在0和-1间选择
只是开始时先比较两个操作数的位数,若S1<S2则交换S1和S2位置,在输出答案前加上“-”;
bug:当两个数位数相同但实际数值S1<S2时,目前会输出“error”,得不出负数结果
还请大佬指点

四:乘法

乘法的计算思路和我们正常做竖式一样
操作数存储和上述一样(我觉得可以考虑改其它数据结构可能会更简便)
计算前根据栈顶存储的操作数长度,将更长的那个放在乘数位置(竖式的上面),另一个放下面,可以减少循环次数。因为乘数会被反复使用,所以直接申请一个数组a单独存放。
每次循环,被乘数栈弹出一个数,同时用time记录是第几次循环(关系到结果后面的补0个数),与a数组从右开始进行一位数的乘法运算,k记录进位,(S2*a[i]+k)%10压入辅助栈t,(S2*a[i]+k)/10给k。每得出一个结果便加到上一个结果上(调用上面加法的方法,本人单独写了一个jiafa_2函数(实现s+t),因为发现操作数是倒过来的,在最前面加了对栈的倒转(swap函数)对于这部分可以考虑把链栈结构改为链队列),最后得到的栈s即为乘法的结果。



完整可运行代码如下:

#include<stdio.h>
#include<stdlib.h>
typedef struct Snode{
    int data;                           //存放一位数字 
    struct Snode *next;
}Snode,*LinkStack;
void Push(LinkStack &S,int e)           //压栈 
{
    LinkStack p;
    p=new Snode;
    p->data=e;
    p->next=S;
    S=p;
}
int Pop(LinkStack &S)                   //出栈 
{
    int e;
    LinkStack p;
    if(S==NULL)return -1;
    e=S->data;
    p=S;
    S=S->next;
    delete p;
    return e;
}
void Putin(LinkStack &S1)                //录入大整数,以#结束 
{
    char A;
    int step=0;                          //step记录大整数位数 
    printf("put in num\n");
    while(1)
    {
        step++;
        scanf("%c",&A);
        if(A=='#')
            break;
        Push(S1,A-48);
    }
    Push(S1,step-1);                      //将step压在栈顶 
}
void jiafa()                              //大整数加法运算 
{
    LinkStack S1;
    S1=NULL;
    Putin(S1);                            //录入大整数S1 
    
    getchar();
    
    LinkStack S2;
    S2=NULL;
    Putin(S2);                            //录入大整数S2 
    
    LinkStack S;
    S=NULL;                               //栈S记录结果 
    int len1=Pop(S1);                     //len1记录S1长度 
    int len2=Pop(S2);                     //len2记录S2长度 
    int k=0;                              //记录是否进位 
    int c;                                //c作为中间量 
    while(S1!=NULL&&S2!=NULL)             //S1、S2都不为空 
    {
        c=Pop(S1)+Pop(S2)+k;              //c为两个栈的栈顶数字之和 
        if(c>=10)
        {
            c=c-10;
            k=1;
            Push(S,c);                    //将c的个位压入栈S 
        }else 
        {
            k=0;
            Push(S,c);
        }
    }
    if(S1==NULL&&S2==NULL&&k!=0)          //当两个栈同时为空,如果k!=0则将k压栈 
    {
        Push(S,k);                         
        k=0;
    }
    while(S1!=NULL)                       //当S1不为空,将S1和k相加后压入S栈 
    {
        c=Pop(S1)+k;
        if(c>=10)
        {
            k=1;
            c=c-10;
            Push(S,c);
        }
        else
        {
            k=0;
            Push(S,c);
        }    
    }
    while(S2!=NULL)                       //当S2不为空,将S1和k相加后压入S栈 
    {
        c=Pop(S2)+k;
        if(c>=10)
        {
            k=1;
            c=c-10;
            Push(S,c);
        }
        else
        {
            k=0;
            Push(S,c);
        }    
    }
    if(k!=0)Push(S,k);                     //当所有栈都为空时,看k!=0时将k压栈 
    while(S!=NULL)                         //针对类似99999+1的情况 
        printf("%d",Pop(S));
}

void jianfa()                              //大整数减法运算 
{
    LinkStack S1;                          //和加法一样的录入过程,默认S1为被减数,S2为减数 
    S1=NULL;
    Putin(S1);
    
    getchar();
    
    LinkStack S2;
    S2=NULL;
    Putin(S2);
    
    LinkStack S;
    S=NULL;
    int len1=Pop(S1);
    int len2=Pop(S2);
    int k=0;
    if(len1<len2)                          //当S1的位数<S2的位数, 
    {                                      // 将S1和S2交换位置,在输出结果时加-号
        S=S1;
        S1=S2;
        S2=S;
        S=NULL;
    }
    while(S1!=NULL&&S2!=NULL)              //S1、S2都不空则循环计算 
    {
        int a=Pop(S1);                     //a记录S1栈顶即减数 
        int b=Pop(S2);                     //b记录S2栈顶即被减数 
        if(a+k>=b)                         //a+k>=b即够减则直接将结果压栈 
        {
            Push(S,a+k-b);
            k=0;
        }
        else                               //不够减则将k置为-1,a加上10进行运算 
        {
            Push(S,a+k+10-b);
            k=-1;            
        }
    }
    while(S1!=NULL)                        //因为前面保证了S1长度大于等于S2,所以只要考虑S1是不是空 
    {
        int a=Pop(S1);
        if(a+k>=0)
        {
            Push(S,a+k);
            k=0;
        }
        else                                //a+k<0即还要向前借位,所以当前位置为9 ,k为-1 
        {
            k=-1;
            Push(S,9);
        }
    }
    if(k==-1)                               //S1、S2都为空,k仍为-1说明S1和S2位数相同但S1<S2,输出error报错 
    {
        printf("error\n");
        return;
    }
    if(k==0){                               //k==0说明S1-S2够减 
        int a=0;
        while((a=Pop(S))==0);               //将结果栈栈顶为0的部分弹出 
        if(a==-1)a=0;                       //如果全部为0则将a置0 
        Push(S,a);                          //将弹出的不为零的项压回去 
    };
    if(len1<len2)printf("-");
    while(S!=NULL)
        printf("%d",Pop(S));
    return;
}
void Swap(LinkStack &S)                     //将栈S首位倒转 
{
    LinkStack T;
    T=NULL;
    while(S!=NULL)
    {
        Push(T,Pop(S));
    }
    S=T;
}
void jiafa_2(LinkStack &S1,LinkStack &S2)    //针对乘法部分单独写的加法函数 
{                                            //将S1和S2相加,结果存入S1 
    Swap(S1);                                //这里获得的S1,S2都是上一次加法的结果,所以顺序正好相反,将S1倒转 
    Swap(S2);                                //将S2倒转 
    LinkStack S;
    S=NULL;
    int k=0;
    int c;
    while(S1!=NULL&&S2!=NULL)                //具体加法过程和上面相同 
    {
        c=Pop(S1)+Pop(S2)+k;
        if(c>=10)
        {
            c=c-10;
            k=1;
            Push(S,c);
        }else 
        {
            k=0;
            Push(S,c);
        }
    }
    if(S1==NULL&&S2==NULL&&k!=0)
    {
        Push(S,k);
        k=0;
    }
    while(S1!=NULL)
    {
        c=Pop(S1)+k;
        if(c>=10)
        {
            k=1;
            c=c-10;
            Push(S,c);
        }
        else
        {
            k=0;
            Push(S,c);
        }    
    }
    while(S2!=NULL)
    {
        c=Pop(S2)+k;
        if(c>=10)
        {
            k=1;
            c=c-10;
            Push(S,c);
        }
        else
        {
            k=0;
            Push(S,c);
        }    
    }
    if(k!=0)Push(S,k);
    S1=NULL;
    S1=S;                                  //把结果栈赋给S1 
}
void chenfa()                              //大整数乘法运算 
{
    LinkStack S1;                          //大整数录入过程和上面相同 
    S1=NULL;
    Putin(S1);
    
    getchar();
    
    LinkStack S2;
    S2=NULL;
    Putin(S2);
        
    LinkStack S;
    S=NULL;
    int c,k=0;
    int time=-1;                           //记录被乘数计算到第几位了 
    int len1=Pop(S1);
    int len2=Pop(S2);
    if(len1<len2)                          //比较S1\S2位数,将长的设为被乘数,即放在前面 
    {
        S=S1;
        S1=S2;
        S2=S;
        S=NULL;
        c=len1;
        len1=len2;
        len2=c; 
    }
    
    int *a=new int[len1];                   //因为被乘数会被循环使用 
    for(int i=len1-1;i>=0;i--)              //所以动态开辟数组将被乘数按位存入其中 
        a[i]=Pop(S1);
    while(S2!=NULL)                         //乘数不为空即要进行一次循环 
    {
        time++;
        c=Pop(S2);                            //c记录乘数栈顶即最低位 
        if(time==0)                         
        {
            for(int i=len1-1;i>=0;i--)      // 从被乘数最低位开始循环 
            {
                Push(S,(c*a[i]+k)%10);      //将结果的各位压栈,十位给k 
                k=(c*a[i]+k)/10;
            }
            if(k!=0)                        //被乘数已经遍历完,k!=0则把k压栈 
                Push(S,k);
        }
        else
        {
            LinkStack t;
            t=NULL;
            for(int i=0;i<time;i++)         //从乘数的第二位开始,要为结果填充0 
                Push(t,0);
            k=0;
            for(int i=len1-1;i>=0;i--)
            {
                Push(t,(c*a[i]+k)%10);
                k=(c*a[i]+k)/10;
            }
            if(k!=0)
                Push(t,k);
            jiafa_2(S,t);
        }    
    }        
    while(S!=NULL)
        printf("%d",Pop(S));
    printf("\n");
    
}
int main()
{
    
    int n;
    printf("1.加法\n2.减法\n3.乘法\n0.退出\n ");
    printf("put in n\n");
    scanf("%d",&n);
    getchar();
    while(n!=0)
    {
        system("cls");
        switch(n)
        {
            case 1:
                {
                    jiafa();
                    break;
                }
            case 2:
                {
                    jianfa();
                    break;
                }
                case 3:
                    {
                        chenfa();
                        break;
                    }
        }
        printf("\n1.加法\n2.减法\n3.乘法\n0.退出\n");
        printf("put in n\n");
        scanf("%d",&n);
        getchar();
    }
}

​
```c


```


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 小数相加
int decimalAdd(char *str1, char *str2, char *str3)
{
    int l1 = (str1 == NULL ? 0 : strlen(str1));
    int l2 = (str2 == NULL ? 0 : strlen(str2));
    int flag = 0; // 是否进位
    int i = l1 > l2 ? l1 : l2;
    int j = 0;
    while (i > 0) {
        int t = flag;
        if (i <= l1) {
            t += str1[i - 1] - '0';
        }
        if (i <= l2) {
            t += str2[i - 1] - '0';
        }
        if (t % 10 != 0 || j != 0) { // 过滤末尾的0
            str3[j++] = '0' + (t % 10);
        }
        flag = t / 10;
        --i;
    }
    if (j == 0) {
        str3[j] = '0';
    }
    return flag;
}

// 整数相加
void subAdd(char *str1, char *str2, int flag, char *str3)
{
    int l1 = (str1 == NULL ? 0 : strlen(str1));
    int l2 = (str2 == NULL ? 0 : strlen(str2));
    int i = 1;
    for (; i <= l1 || i <= l2; ++i) {
        int t = flag;
        if (i <= l1) {
            t += str1[l1 - i] - '0';
        }
        if (i <= l2) {
            t += str2[l2 - i] - '0';
        }
        str3[i - 1] = '0' + (t % 10);
        flag = t / 10;
    }
    if (flag) {
        str3[i - 1] = '0' + flag;
    }
    return;
}

void add(char *str1, char *str2, char *str3)
{
    int l1 = strlen(str1);
    int l2 = strlen(str2);
    char *p1 = strchr(str1, '.'); // 整数部分
    char *decimal1 = NULL;
    if (p1 != NULL) {
        *p1 = '\0';
        decimal1 = p1 + 1;
    }
    char *p2 = strchr(str2, '.');
    char *decimal2 = NULL;
    if (p2 != NULL) {
        *p2 = '\0';
        decimal2 = p2 + 1;
    }
    int flag = decimalAdd(decimal1, decimal2, str3);
    int tlen = strlen(str3);
    str3[tlen] = '.';
    subAdd(str1, str2, flag, str3 + tlen + 1);
    // 反转str3
    for (int i = 0, j = strlen(str3) - 1; i < j; ++i, --j) {
        char t = str3[i];
        str3[i] = str3[j];
        str3[j] = t;
    }
    printf("%s\n", str3);
}

int main(void)
{
    int n;
    scanf("%d", &n);
    char left[128] = {0};
    char right[128] = {0};
    char result[256] = {0};
    for (int i = 0; i < n; ++i) {
        scanf("%s", left);
        scanf("%s", right);
        memset(result, 0, sizeof(result));
        add(left, right, result);
    }
    return 0;
}