C语言 大整数运算(加、减、乘)

题目:大整数计算

  • 背景介绍:
    大整数一般指超过十尾的十进制整数,假定不超过五十位。这类大整数在C语言系统中因超界溢出而不能直接表达或计算。
  • 实现方法:
    以字符串形式输入、输出和存放大整数,计算时可以将字符串中的每一位数字转换成相应的数值进行运算。
  • 功能要求:
    主函数输入两个大整数,并提供功能菜单供用户选择,用户可以选择调用各个运算功能,对两个大整数进行相应的运算,并输出结果。也可以选择继续或退出程序。
    功能:1. 加法 2. 减法 3. 乘法
  • 代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <conio.h>
#define MAX 51  //题目假设大整数最高为50位,所以取长度为MAX(51)的字符数组

void format(int *a) //升位: 低位除10剩余数,高位加商;比如十位的13,变成百位加1和十位剩3
{
    int p;
    for(p = 1; p < a[0] || a[p] >= 10; p++)
    {
        if (p >= a[0])
            a[p+1] = 0;
        a[p+1] += (a[p] / 10); //高位加上除10运算的商
        a[p] = a[p] % 10; //低位等于除10运算的余数
    }
    if (p >= a[0])
        a[0] = p;
}

void add(int *a,int *b,int *c)
{
    int len,i;

    if (a[0] < b[0])
        len=a[0];
    else
        len=b[0];

    for(i = 1; i <= len; i++)
    {
        c[i] = a[i] + b[i];
    }
    if (len < a[0])
    {
        for( ; i <= a[0]; i++)
        {
            c[i] = a[i];
        }
        c[0] = a[0];
    }
    else
    {
        for( ; i <= b[0]; i++)
        {
            c[i] = b[i];
        }
        c[0] = b[0];
    }
    format(c);
}

void commaltiply (int *a, int x, int *b)
{
    int i;
    for(i = 1; i <= a[0]; i++)
    {
        b[i] = a[i] * x;
    }
    b[0] = a[0];
    format(b);
}


void matiply(int *a,int *b,int *c)
{
    int i,temp[MAX],j;
    commaltiply(a, b[1], c);

    for(i = 2; i <= b[0]; i++)
    {
        if(b[i] == 0)
        {
            c[0]++;
            for (j = c[0]; j > 1; j--)
                c[1] = 0;
        }
        else
        {
            commaltiply(a, b[i], temp);
            for(j = 1; j < i; j++)
                commaltiply(temp, 10, temp);

            add(c, temp, c);
        }
    }
}


void format1(int *a) //降位: 高位减1 补到 低位成10;比如百位的1相当于十位的10
{
    int i;
    for(i = 1; i <= a[0]; i++)
    if(a[i] < 0)
    {
        a[i] += 10;
        a[i+1] -= 1;
    }
}

void sub_1(int *a,int *b,int *c) //默认形参a比b长的减法运算
{
    int i;
    for(i = 1; i <= b[0]; i++)
        c[i] = a[i] - b[i];
    if(a[0] != b[0])
    {
    	//初始时 i = b[0],从b[0]开始+1,最终加到a[0]
        for ( ; i <= a[0]; i++)
            c[i] = a[i];
    }
    c[0] = a[0];
    format1(c);
    i = c[0]; //先让i暂存运算结果的位数(长度)
    while(1)
    {
        if(c[i] == 0) //从高位开始检查,如果高位数字为0,则 位数-1
            i--;
        else //当检查到某较高位数不为0,则退出while循环
            break;
    }
    c[0] = i; //c[0] 存储检查后的位数
}

void sub(int *a, int *b, int *c)  //减法
{
    int i, flag = 0; //flag用来标记 两个大整数是否相等的
    for(i = 1; i <= a[0]; i++) //a[0] 是 大整数的长度
	  	if(a[i] != b[i]) //两个大整数只要有一位数字不相等,则flag = 1
   	  	{
       		flag = 1;
        	break;
    	}
    if(flag == 0) //如果两个大整数完全相等,则flag不变还是0 则计算结果为0
    {
        c[0] = 0;
        return;
    }
    flag = 0; //此时flag用来标记 a和b运算减法运算的情况
    if(a[0] == b[0]) //如果两个大整数的长度相同
    {
        for(i = a[0]; i > 0; i--) //从最高位开始比较
        {
            if(a[i] > b[i]) //如果a的最高位数字比b的大
            {
                sub_1(a,b,c); //正常执行减法运算
                break;
            }
            if(a[i] < b[i]) //如果a的最高位数字比b的小,b减a,flag=1
            {
                flag = 1;
                sub_1(b,a,c);
                break;
            }
        }
    }
    if(a[0] > b[0]) //如果a,b的长度不等,且a比b长,直接计算
    {
        sub_1(a,b,c);
    }
    else if (a[0] < b[0]) //如果a,b的长度不等,且a比b短,执行b减a
    {
        flag = 1;
        sub_1(b,a,c);
    }
    if (flag) //执行b减a运算的时候结果应该取相反数
        c[0] = -c[0];
    else
        c[0] = c[0];
}

void read_in(int *a) //读入长整数
{
    int len, i;
    int flag = 0; // flag用来标记负数的存在,正数记0,负数记1
    int flag_error = 0;
    char s[MAX];
    //如果读入的是负长整数,则字符数组的第一个位置要存储负号
    //而且长整数的最大位数为50位,因此需要长度为51的字符数组
    //正长整数则不用担心这个问题
    while(1)
    {
        scanf("%s",s);
        len = strlen(s);
        if (s[0] == '-') flag = 1; //标记负数
        for(i = len-flag; i >= 1; i--)  //倒序存储
        {
            if ((s[len-i]-'0') < 0||(s[len-i]-'0') > 9)
            {
                printf("\t\t\t 第%d位输入错误,按任意键重新输入!",len-i+1);
                getch();
                flag_error++;
                break;
                //某位错误,错误标记记1,不再检查其他位数,跳出for循环,检查flag_error
            }
            else
                a[i] = s[len-i] - '0';
            /* 如果第i位没错,把该位的数字字符转换成数字存入a[]数组中
            *  s[len-i]-'0'是利用 数字字符的ASCII码和数字本身数值大小的关系
            *  来实现 字符转换成数字 的功能 */
        } // end for
        if(flag_error)
            continue;
        //continue:不执行后面的语句,重新开始一次while循环
        if (flag)
            a[0] = -(len-1);
        else
            a[0] = len;

        break;
    } // end while
}

void print(int *a)
{
    int i;
    if (a[0] < 0)
    {
        printf("-");
    }
    if (a[0] == 0)
    {
        printf("0");
        return;
    }
    for(i = abs(a[0]); i >= 1; i--) //倒序输出
        printf("%d",a[i]);
    printf("\n");
}

char menu() //功能菜单
{
    char ans; // answer的简写,用于存储用户输入的回答
    while(1)
    {
        printf("\n\n\n");
        printf("\t\t\t\t————————————————\n");
        printf("\t\t\t\t|       > 长整数计算器 <       |\n");
        printf("\t\t\t\t|                              |\n");
        printf("\t\t\t\t|        1.大整数的加法        |\n");
        printf("\t\t\t\t|        2.大整数的减法        |\n");
        printf("\t\t\t\t|        3.大整数的乘法        |\n");
        printf("\t\t\t\t|        4.退出                |\n");
        printf("\t\t\t\t|                              |\n");
        printf("\t\t\t\t————————————————\n");
        printf("\n\n\n\t\t\t      请选择功能(1-4): ");

        ans = getchar();
        if(ans == '1'||ans == '2'||ans == '3'||ans == '4')
            break;
        else
        {
            getch();
            printf("\n\t\t\t  输入错误!按任意键重新输入!!!!");
            getch();
            system("cls");
        }
    }
    return ans;
}

int main()
{
    int a[MAX],b[MAX],c[MAX];
    // a存储第一个长整数,b存储第二个,c存储运算结果
    char ans; // answer的简写,用于存储用户输入的回答

    while(1)
    {
        ans = menu();
        if(ans == '4') return 0; //先判断是否要退出

        printf("\n\t\t\t  请输入大整数A: ");
        read_in(a);
        printf("\n\t\t\t  请输入大整数B: ");
        read_in(b);

        if (ans == '1')
        {
            if (a[0] < 0 && b[0] < 0)
            {
                a[0] = -a[0];
                b[0] = -b[0];
                add(a,b,c);
                c[0] = -c[0];
                a[0] = -a[0];
                b[0] = -b[0];
            }
            if (a[0] > 0 && b[0] < 0)
            {
                b[0] = -b[0];
                sub(a,b,c);
                b[0] = -b[0];
            }
            else if (a[0] > 0 && b[0] > 0)
            {
                add(a,b,c);
            }
            else if (a[0] < 0 && b[0] > 0)
            {
                a[0] = -a[0];
                sub(b,a,c);
                a[0] = -a[0];
            }
            printf("\n\t\t\t A = ");
            print(a);
            printf("\n\t\t\t B = ");
            print(b);
            printf("\n\t\t\t C = A+B = ");
            print(c);

        } // end if(rn=='1')

        else if (ans == '2')
        {
            if (a[0] < 0 && b[0] < 0)
            {
                a[0] = -a[0];
                b[0] = -b[0];
                sub(b,a,c);
                a[0] = -a[0];
                b[0] = -b[0];
            }
            if (a[0] < 0 && b[0] > 0)
            {
                a[0] = -a[0];
                add(a,b,c);
                a[0] = -a[0];
                c[0] = -c[0];
            }
            if (a[0] > 0 && b[0] < 0)
            {
                b[0] = -b[0];
                add(a,b,c);
                b[0] = -b[0];
            }
            if (a[0] > 0 && b[0] > 0)
                sub(a,b,c);

            printf("\n\t\t\t A = ");
            print(a);
            printf("\n\t\t\t B = ");
            print(b);
            printf("\n\t\t\t C = A-B = ");
            print(c);

        } // end if(rn=='2')

        else if (ans == '3')
        {
            if (a[0] < 0 && b[0] > 0)
            {
                a[0] = -a[0];
                matiply(a,b,c);
                a[0] = -a[0];
                c[0] = -c[0];
            }
            else if (a[0] > 0 && b[0] < 0)
            {
                b[0] = -b[0];
                matiply(a,b,c);
                b[0] = -b[0];
                c[0] = -c[0];
            }
            else if (a[0] < 0 && b[0] < 0)
            {
                a[0] = -a[0];
                b[0] = -b[0];
                matiply(a,b,c);
                a[0] = -a[0];
                b[0] = -b[0];
            }
            else
                matiply(a,b,c);

            printf("\n\t\t\t A = ");
            print(a);
            printf("\n\t\t\t B = ");
            print(b);
            printf("\n\t\t\t C = A*B = ");
            print(c);

        } // end if(rn=='3')

        while(1)
        {
            printf("\n\t\t\t 是否继续计算?(y/n): ");
            getchar();
            ans = getchar();
            if(ans == 'y' || ans == 'Y')
            {
                system("cls");
                getchar();
                break;
            }
            else
                return 0;

        } // end inside while

    } // end outside while

} // end main

  • 代码分析:

    • 关于sub_1与sub函数关系:
      sub_1就像一个机器一样,它不管你怎么投入的数字,他只会按照固定的顺序(第1个减第2个的结果存入第3个)来计算,产出结果,
      而sub就有点“智能”,根据a和b不同的情况,来调整投入sub_1机器的数字顺序(abc还是bac)
      总结:sub是“操作机器的人”,sub_1是“机器”
  • 运行结果:

在这里插入图片描述

  • 存在的缺陷:
    • 健壮性较弱,例如菜单测试用例为"b2"时,运行结果如下:

在这里插入图片描述

  • 错误分析:
    第一个"b"被当作菜单的输入,去匹配功能选项(1-4),因为是错误输入调用了清屏函数,而"2"留在缓存区被当作下一次的菜单输入匹配到功能2,因此执行了长整数减法。
    类似的输入测试用例还有"1111abcn"等等
  • 19
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
C语言中可以使用大整数库来进行大整数运算,其中比较常用的是GMP库(GNU Multiple Precision Arithmetic Library)。 使用GMP库进行大整数运算的步骤如下: 1. 引入头文件:`#include<gmp.h>` 2. 定义大整数变量:`mpz_t num;` 3. 初始化大整数变量:`mpz_init(num);` 4. 进行运算,例如法:`mpz_add(num, num1, num2);` 5. 输出结果:`gmp_printf("%Zd", num);` 其中,`num1`和`num2`为两个大整数变量,`%Zd`是gmp_printf函数用于输出大整数的格式控制符。 以下是使用GMP库进行乘除幂运算的代码示例: ```c #include <stdio.h> #include <gmp.h> int main() { mpz_t num1, num2, result; mpz_init(num1); mpz_init(num2); mpz_init(result); // 法 mpz_set_str(num1, "12345678901234567890", 10); mpz_set_str(num2, "98765432109876543210", 10); mpz_add(result, num1, num2); gmp_printf("法结果:%Zd\n", result); // 法 mpz_set_str(num1, "98765432109876543210", 10); mpz_set_str(num2, "12345678901234567890", 10); mpz_sub(result, num1, num2); gmp_printf("法结果:%Zd\n", result); // 乘法 mpz_set_str(num1, "12345678901234567890", 10); mpz_set_str(num2, "98765432109876543210", 10); mpz_mul(result, num1, num2); gmp_printf("乘法结果:%Zd\n", result); // 除法 mpz_set_str(num1, "98765432109876543210", 10); mpz_set_str(num2, "12345678901234567890", 10); mpz_tdiv_q(result, num1, num2); gmp_printf("除法结果:%Zd\n", result); // 幂运算 mpz_set_str(num1, "123456789", 10); mpz_set_str(num2, "10", 10); mpz_pow_ui(result, num1, mpz_get_ui(num2)); gmp_printf("幂运算结果:%Zd\n", result); mpz_clear(num1); mpz_clear(num2); mpz_clear(result); return 0; } ``` 需要注意的是,GMP库的使用需要在编译时链接相应的库文件,例如在Linux系统下,可以使用以下命令编译上述代码:`gcc -o test test.c -lgmp`。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值