C语言实现大数计算器

C语言实现大数计算器

一、实验介绍

1.1 实验内容

本实验通过使用C语言实现一个简易计算器,该计算器能够进行任意长度的有符号整数运算。

1.2 实验知识点

  • c语言大数加法实现
  • c语言大数减法实现
  • c语言大数乘法实现
  • c语言大数除法实现

1.3 实验环境

  • vim
  • gcc 编译器
  • Xfce终端

1.4 适合人群

本课程适合已了解C语言基础语法规则,并期望通过实际编程项目进行演练,以提高C语言编程技巧的用户进行学习。

1.5 代码获取

你可以通过下面命令将代码下载到实验楼环境中,作为参照对比进行学习。

$ wget http://labfile.oss.aliyuncs.com/courses/750/calculator_sample.tar.gz
$ tar xvf calculator_sample.tar.gz

二、实验步骤

2.1 如何表示大数

由于C语言的基本数据类型在表示数字大小时都有一定的限制,所以要在C语言中表示一个大数,有两种方式:字符串和自行构造数据结构。

  1. 字符串表示,适合人解读,但不适用于机器运算

  2. 数据结构表示,适用于机器运算,但人对其进行解读比较困难

我们的设计,综合考虑上述两种方式的优缺点,我们自行构造数据用于表示大数以提供给机器进行运算,同时提供字符串与我们构造的数据结构进行互转的辅助性函数:

// 大数数据结构
typedef struct bignumber_s{
    char sign; // 代表大数的符号,1为负数,0为正数
    int len;   // 代表大数的位数
    char data[]; // 大数的数据内容,data[0]代表个位,data[1]代表十位,data[2]代表百位....
} bignumber_s;

// 构造大数模板,len指向大数的数据位数,sign表示该大数的符号
bignumber_s *make_bignumber_temp(int len, int sign);
// 从字符串构造一个大数
bignumber_s *make_bignumber_fromstr(const char *str);
// 以字符串的形式打印输出一个大数
void print_bignumber(bignumber_s* b);

bignumber_s数据结构的定义中,我们使用了C语言里一种称为柔性数组的技巧,感兴趣的同学可以自行研究。

下图演示了我们的bignumber_s如何表示"-123"这个数:

此处输入图片的描述

实现make_bignumber_temp函数:

// 构造大数模板,len指向大数的数据位数,sign表示该大数的符号
bignumber_s *make_bignumber_temp(int len, int sign)
{
    // 分配bignumber_s及其所代表数据 所需的内存
    bignumber_s *temp = malloc(sizeof(bignumber_s)+len);
    if (NULL == temp) {
        perror("Malloc");
        exit(-1);
    }
    temp->sign = sign;
    temp->len  = len;
    memset(temp->data, 0, len);
    return temp;
}

实现make_bignumber_fromstr函数:

// 从字符串构造一个大数
bignumber_s *make_bignumber_fromstr(const char *str)
{
    // 处理负数字符串,即"-123"
    int sign = 0;
    if (str[0]=='-') {
        sign = 1;
        str++;
    }
    // 处理数字字符串冗余的0,即"00123" -> "123"
    const char * striped_str = strip_str(str);

    int len = strlen(striped_str);
    // 指定数据位长度即符号,创建一个大数模板
    bignumber_s *temp = make_bignumber_temp(len ,sign);
    // 将字符串数据填充进模板中,完成大数创建
    fill_data_fromstr(temp, striped_str);
    return temp;
}
// 处理数字字符串冗余的0,即"00123" -> "123"
const char *strip_str(const char *str)
{
    int i = 0;
        int len = strlen(str);
        for (i=0; i<len-1&&str[i]=='0'; i++) ;
        return str+i;
}
// 将字符串数据填充进模板中
void fill_data_fromstr(bignumber_s *n, const char *str)
{
    int i = 0;
    int len = n->len;
    for (i=0; i<len; i++) {
        int d = str[len-1-i]-'0';
        if (d>=0 && d<=9) 
            n->data[i] = d;
        else {
          fprintf(stderr, "Invalid Number:%s\n", str);
          exit(-1);
        }
    }
}

实现print_bignumber函数:

// 以字符串的形式打印输出一个大数
void print_bignumber(bignumber_s* b)
{
   int len = b->len;
   char *str = malloc(len+1);
   int i = 0;

   for (i=0; i<len; i++) {
      str[i] = b->data[len-i-1]+'0';
   }
   str[len] = '\0';

   fprintf(stdout,"%s%s\n", b->sign==1?"-":"", strip_str(str));
   free(str);
}

2.2 实现计算器的壳

我们的计算器最终编译生成为一个名为calculator的可执行程序,使用计算器的方法如下:

$ ./calculator 123 + 123 # 执行加法运算
$ ./calculator 123 - 123 # 执行减法运算
$ ./calculator 123 x 123 # 执行乘法运算
$ ./calculator 123 / 123 # 执行除法运算

实现我们的main函数:

void usage(const char *s)
{
    fprintf(stderr, "Usage:%s number1 op(+-x/) number2.\n",s);
    exit(-1);
}

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b)
{
  // 实现加法
}
bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b)
{
  // 实现减法
}
bignumber_s *calc_mul(bignumber_s *a, bignumber_s *b)
{
  // 实现乘法
}
bignumber_s *calc_div(bignumber_s *a, bignumber_s *b)
{
  // 实现除法
}
int main(int argc, char *argv[])
{
    bignumber_s *a = make_bignumber_fromstr(argv[1]);
    bignumber_s *b = make_bignumber_fromstr(argv[3]);
    if (argc!=4) usage(argv[0]);
    if (0 == strcmp(argv[2],"+"))
        print_bignumber(calc_add(a,b));
    else if (0 == strcmp(argv[2],"-"))
        print_bignumber(calc_sub(a,b));
    else if (0 == strcmp(argv[2],"x"))
        print_bignumber(calc_mul(a,b));
    else if (0 == strcmp(argv[2],"/"))
        print_bignumber(calc_div(a,b));
    else usage(argv[0]);
    return 0;
}

2.3 实现无符号加减法

2.3.1 实现无符号加法

加法的实现比较简单,我们借鉴小学时学过的竖式计算过程的思路:

此处输入图片的描述

思路大致是:

  1. 从最低位开始,对应位置相加,和大于10的向高位进位,取个位数为该位置相加的和
  2. 对应位置相加时,要加上来自低位的进位值
  3. n位数+m位数,其和最大为max(n,m)+1位数

实现无符号加法:

#define BASE (10)
#define MAX(x,y) ((x)>(y)?(x):(y))
// 实现无符号加法,a和b为加数,r为和
void add_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
    int i = 0;
    char carry = 0;
    int len = r->len;
    for (i=0; i<len; i++) {
       if (i<a->len)carry += a->data[i];
       if (i<b->len)carry += b->data[i];
       r->data[i] = carry%BASE;
       carry /= BASE;
    }
}

bignumber_s *add(bignumber_s *a, bignumber_s *b)
{
    // n位数+m位数,其和最大为max(n,m)+1位数
    int len = MAX(a->len, b->len) + 1;
    bignumber_s *result = make_bignumber_temp(len,a->sign);
    // 
    add_impl(a,b,result);
    return result;
}
2.3.2 实现无符号减法

减法的实现与加法的实现思路相似,依旧请出的我们竖式计数法:

此处输入图片的描述

思路大致是:

  1. 从最低位开始,对应位置相减,不够减的向高位借位,得到的差作为该位置的差
  2. 对应位置相减时,要减去来自低位的借位值
  3. n位数-m位数,其和最大为n位数

实现无符号减法:

// 实现无符号减法,a-b , r为差
void sub_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
        int i=0;
        int borrow = 0;
        int len = r->len;
        int temp = 0;
        for (i=0; i<len; i++) {
            temp = a->data[i]+BASE-borrow-((i<b->len)?b->data[i]:0);
            r->data[i] = temp%BASE;
            borrow = 1-temp/BASE;
        }
}

bignumber_s *sub(bignumber_s *a, bignumber_s *b)
{
   int len = a->len;
   bignumber_s *result = make_bignumber_temp(len,0);
   sub_impl(a,b,result);
   return result;
}
2.3.3 大数计算器v1版本

大数计算器v1版本:

/*
* file name : calculatorv1.c
* desp : calculator version 1
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BASE (10)
#define MAX(x,y) ((x)>(y)?(x):(y))

// 大数数据结构
typedef struct bignumber_s{
    char sign; // 代表大数的符号,1为负数,0为正数
    int len;   // 代表大数的位数
    char data[]; // 大数的数据内容,data[0]代表个位,data[1]代表十位,data[2]代表百位....
} bignumber_s;

// 构造大数模板,len指向大数的数据位数,sign表示该大数的符号
bignumber_s *make_bignumber_temp(int len, int sign)
{
    // 分配bignumber_s及其所代表数据 所需的内存
    bignumber_s *temp = malloc(sizeof(bignumber_s)+len);
    if (NULL == temp) {
        perror("Malloc");
        exit(-1);
    }
    temp->sign = sign;
    temp->len  = len;
    memset(temp->data, 0, len);
    return temp;
}

// 处理数字字符串冗余的0,即"00123" -> "123"
const char *strip_str(const char *str)
{
    int i = 0;
        int len = strlen(str);
        for (i=0; i<len-1&&str[i]=='0'; i++) ;
        return str+i;
}
// 将字符串数据填充进模板中
void fill_data_fromstr(bignumber_s *n, const char *str)
{
    int i = 0;
    int len = n->len;
    for (i=0; i<len; i++) {
        int d = str[len-1-i]-'0';
        if (d>=0 && d<=9) 
            n->data[i] = d;
        else {
          fprintf(stderr, "Invalid Number:%s\n", str);
          exit(-1);
        }
    }
}
// 从字符串构造一个大数
bignumber_s *make_bignumber_fromstr(const char *str)
{
    // 处理负数字符串,即"-123"
    int sign = 0;
    if (str[0]=='-') {
        sign = 1;
        str++;
    }
    // 处理数字字符串冗余的0,即"00123" -> "123"
    const char * striped_str = strip_str(str);

    int len = strlen(striped_str);
    // 指定数据位长度即符号,创建一个大数模板
    bignumber_s *temp = make_bignumber_temp(len ,sign);
    // 将字符串数据填充进模板中,完成大数创建
    fill_data_fromstr(temp, striped_str);
    return temp;
}

// 以字符串的形式打印输出一个大数
void print_bignumber(bignumber_s* b)
{
   int len = b->len;
   char *str = malloc(len+1);
   int i = 0;

   for (i=0; i<len; i++) {
      str[i] = b->data[len-i-1]+'0';
   }
   str[len] = '\0';

   fprintf(stdout,"%s%s\n", b->sign==1?"-":"", strip_str(str));
   free(str);
}

void usage(const char *s)
{
    fprintf(stderr, "Usage:%s number1 +-x/ number2.\n",s);
    exit(-1);
}

// 实现无符号加法,a和b为加数,r为和
void add_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
    int i = 0;
    char carry = 0;
    int len = r->len;
    for (i=0; i<len; i++) {
       if (i<a->len)carry += a->data[i];
       if (i<b->len)carry += b->data[i];
       r->data[i] = carry%BASE;
       carry /= BASE;
    }
}

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b)
{
    // n位数+m位数,其和最大为max(n,m)+1位数
    int len = MAX(a->len, b->len) + 1;
    bignumber_s *result = make_bignumber_temp(len, 0
                                             );
    // 
    add_impl(a,b,result);
    return result;
}

// 实现无符号减法,a-b , r为差
void sub_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
        int i=0;
        int borrow = 0;
        int len = r->len;
        int temp = 0;
        for (i=0; i<len; i++) {
            temp = a->data[i]+BASE-borrow-((i<b->len)?b->data[i]:0);
            r->data[i] = temp%BASE;
            borrow = temp/BASE?0:1;
        }
}

bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b)
{
   int len = a->len;
   bignumber_s *result = make_bignumber_temp(len,0);
   sub_impl(a,b,result);
   return result;
}

bignumber_s *calc_mul(bignumber_s *a, bignumber_s *b)
{
  // 实现乘法
  return NULL;
}
bignumber_s *calc_div(bignumber_s *a, bignumber_s *b)
{
  // 实现除法
  return NULL;
}
int main(int argc, char *argv[])
{
    bignumber_s *a = make_bignumber_fromstr(argv[1]);
    bignumber_s *b = make_bignumber_fromstr(argv[3]);
    if (argc!=4) usage(argv[0]);

    if (0 == strcmp(argv[2],"+"))
        print_bignumber(calc_add(a,b));
    else if (0 == strcmp(argv[2],"-"))
        print_bignumber(calc_sub(a,b));
    else if (0 == strcmp(argv[2],"x"))
        print_bignumber(calc_mul(a,b));
    else if (0 == strcmp(argv[2],"/"))
        print_bignumber(calc_div(a,b));
    else usage(argv[0]);
    return 0;
}
2.3.4 演示

编译命令:

$ gcc -o calculatorv1 calculatorv1.c

结果演示: 此处输入图片的描述

可以使用python交互界面验证我们的计算结果:

此处输入图片的描述
此处输入图片的描述

2.4 实现有符号加减法

2.4.1 有符号加法

先来看有符号的加法,假设有两个数A和B,有符号加法分以下几种情况:

// 情况一:A > 0 并且 B > 0
A + B --> |A| + |B|   (|A|表示求A的绝对值,下同)
// 情况二:A > 0 并且 B < 0
A + B --> |A| - |B|
// 情况三:A < 0 并且 B > 0
A + B --> |B| - |A|
// 情况四:A < 0 并且 B < 0
A + B --> -(|B| + |A|)  (此处-表示取负值)

情况一和情况四可以用第2节的无符号加法求解,情况四在用加法求值后再取负值;情况二和情况三转化为有符号减法操作。

因此我们的加法函数可以这么实现:

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b)
{
    // 情况一和情况四
    if (a->sign==b->sign) {
        // n位数+m位数,其和最大为max(n,m)+1位数
        int len = MAX(a->len, b->len) + 1;
        bignumber_s *result = make_bignumber_temp(len,a->sign);

        add_impl(a,b,result);
        return result;
    } else if (a->sign == 0 && b->sign == 1) {//情况二
        b->sign = 0; //b数去绝对值
        return calc_sub(a,b);
    } else if (a->sign == 1 && b->sign == 0) {//情况三
        a->sign = 0; //a数去绝对值
        return calc_sub(b,a);
    }
}
2.4.2 有符号减法

接下来看有符号的减法,假设有两个数A和B,有符号减法分以下几种情况:

// 情况一:A > 0 并且 B > 0
A - B --> |A| - |B|   (|A|表示求A的绝对值,下同)
// 情况二:A > 0 并且 B < 0
A - B --> |A| + |B|
// 情况三:A < 0 并且 B > 0
A - B --> -(|A| + |B|)  (此处-表示取负值)
// 情况四:A < 0 并且 B < 0
A - B --> |B| - |A|

情况二和情况三可以转化为第2节的无符号加法求解,情况三在用加法求值后再取负值;

情况一,当被减数大于减数时,结果为正数,可以直接用第2节的减法实现求解;

当被减数小于减数时,结果为负数,即:

// A < B, A>=0 且B>=0 时
A - B ---> -(B-A)

情况四,将减数和被减数调换就成了情况一了。

我们需要先实现一个用以判断两个大数大小的辅助函数,用以判断被减数和减数的大小:

int valid_len(bignumber_s *a)
{
    int len = a->len;
    int i = len-1;
    for (i=len-1; i>=0; i--) {
        if (a->data[i]==0) len--;
        else break;
    }
    return len;
}
// 判断两个大数的大小
// a > b 返回1 ,a<b 返回 -1 , a==b 返回0
int cmp(bignumber_s *a, bignumber_s *b)
{

        if (a->sign==0&&b->sign==1) return 1;
        if (a->sign==1&&b->sign==0) return -1;

        int sign = a->sign;
        int alen = valid_len(a);
        int blen = valid_len(b);
        if (alen>blen) return (sign==1?-1:1);
        else if (alen<blen) return (sign==1?1:-1);
        else {
                int i = 0;
                int len = alen;
                for (i=len-1; i>=0; i--) {
                 if (a->data[i]>b->data[i])return (sign==1?-1:1);
                 else if (a->data[i]<b->data[i]) return (sign==1?1:-1);
            }
            return 0;
        }
}

有符号的减法实现如下:

bignumber_s *sub(bignumber_s *a, bignumber_s *b)
{
    // 情况一
    if(a->sign==0 && b->sign==0) {
        if (cmp(a,b)>=0) { // 被减数大于等于减数,即差大于等于0时
                int len = a->len;
                bignumber_s *result = make_bignumber_temp(len,0);
                sub_impl(a,b,result);
                return result;
        } else { // 被减数小于减数,即差小于0时
                int len = b->len;
                bignumber_s *result = make_bignumber_temp(len,1);
                sub_impl(b,a,result);
                return result;
        }
    } else if (a->sign==1 && b->sign==1) { //情况四
        b->sign=0;  
        a->sign=0;
        return sub(b,a); //调换减数和被减数,变成情况一
    }else if (a->sign==0 && b->sign==1) { // 情况二
        b->sign=0;
        bignumber_s *result = add(a,b);
        result->sign = 0;
        return result; 
    } else if (a->sign==1 && b->sign==0) { // 情况三
        a->sign=0;
        bignumber_s *result = add(a,b);
        result->sign = 1;
        return result; 
    } 
}
2.4.3 大数计算器v2版本

完整代码如下(calculatorv2.c):

/*
* file name : calculatorv2.c
* desp : calculator version 2
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BASE (10)
#define MAX(x,y) ((x)>(y)?(x):(y))

// 大数数据结构
typedef struct bignumber_s{
    char sign; // 代表大数的符号,1为负数,0为正数
    int len;   // 代表大数的位数
    char data[]; // 大数的数据内容,data[0]代表个位,data[1]代表十位,data[2]代表百位....
} bignumber_s;
bignumber_s *calc_add(bignumber_s *a, bignumber_s *b);
bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b);


// 构造大数模板,len指向大数的数据位数,sign表示该大数的符号
bignumber_s *make_bignumber_temp(int len, int sign)
{
    // 分配bignumber_s及其所代表数据 所需的内存
    bignumber_s *temp = malloc(sizeof(bignumber_s)+len);
    if (NULL == temp) {
        perror("Malloc");
        exit(-1);
    }
    temp->sign = sign;
    temp->len  = len;
    memset(temp->data, 0, len);
    return temp;
}

// 处理数字字符串冗余的0,即"00123" -> "123"
const char *strip_str(const char *str)
{
    int i = 0;
        int len = strlen(str);
        for (i=0; i<len-1&&str[i]=='0'; i++) ;
        return str+i;
}
// 将字符串数据填充进模板中
void fill_data_fromstr(bignumber_s *n, const char *str)
{
    int i = 0;
    int len = n->len;
    for (i=0; i<len; i++) {
        int d = str[len-1-i]-'0';
        if (d>=0 && d<=9) 
            n->data[i] = d;
        else {
          fprintf(stderr, "Invalid Number:%s\n", str);
          exit(-1);
        }
    }
}
// 从字符串构造一个大数
bignumber_s *make_bignumber_fromstr(const char *str)
{
    // 处理负数字符串,即"-123"
    int sign = 0;
    if (str[0]=='-') {
        sign = 1;
        str++;
    }
    // 处理数字字符串冗余的0,即"00123" -> "123"
    const char * striped_str = strip_str(str);

    int len = strlen(striped_str);
    // 指定数据位长度即符号,创建一个大数模板
    bignumber_s *temp = make_bignumber_temp(len ,sign);
    // 将字符串数据填充进模板中,完成大数创建
    fill_data_fromstr(temp, striped_str);
    return temp;
}

// 以字符串的形式打印输出一个大数
void print_bignumber(bignumber_s* b)
{
   int len = b->len;
   char *str = malloc(len+1);
   int i = 0;

   for (i=0; i<len; i++) {
      str[i] = b->data[len-i-1]+'0';
   }
   str[len] = '\0';

   fprintf(stdout,"%s%s\n", b->sign==1?"-":"", strip_str(str));
   free(str);
}

void usage(const char *s)
{
    fprintf(stderr, "Usage:%s number1 +-x/ number2.\n",s);
    exit(-1);
}

// 实现无符号加法,a和b为加数,r为和
void add_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
    int i = 0;
    char carry = 0;
    int len = r->len;
    for (i=0; i<len; i++) {
       if (i<a->len)carry += a->data[i];
       if (i<b->len)carry += b->data[i];
       r->data[i] = carry%BASE;
       carry /= BASE;
    }
}

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b)
{
    // 情况一和情况四
    if (a->sign==b->sign) {
        // n位数+m位数,其和最大为max(n,m)+1位数
        int len = MAX(a->len, b->len) + 1;
        bignumber_s *result = make_bignumber_temp(len,a->sign);

        add_impl(a,b,result);
        return result;
    } else if (a->sign == 0 && b->sign == 1) {//情况二
        b->sign = 0; //b数去绝对值
        return calc_sub(a,b);
    } else if (a->sign == 1 && b->sign == 0) {//情况三
        a->sign = 0; //a数去绝对值
        return calc_sub(b,a);
    }
}

// 实现无符号减法,a-b , r为差
void sub_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
        int i=0;
        int borrow = 0;
        int len = r->len;
        int temp = 0;
        for (i=0; i<len; i++) {
            temp = a->data[i]+BASE-borrow-((i<b->len)?b->data[i]:0);
            r->data[i] = temp%BASE;
            borrow = temp/BASE?0:1;
        }
}

int valid_len(bignumber_s *a)
{
    int len = a->len;
    int i = len-1;
    for (i=len-1; i>=0; i--) {
        if (a->data[i]==0) len--;
        else break;
    }
    return len;
}
// 判断两个大数的大小
// a > b 返回1 ,a<b 返回 -1 , a==b 返回0
int cmp(bignumber_s *a, bignumber_s *b)
{

        if (a->sign==0&&b->sign==1) return 1;
        if (a->sign==1&&b->sign==0) return -1;

        int sign = a->sign;
        int alen = valid_len(a);
        int blen = valid_len(b);
        if (alen>blen) return (sign==1?-1:1);
        else if (alen<blen) return (sign==1?1:-1);
        else {
                int i = 0;
                int len = alen;
                for (i=len-1; i>=0; i--) {
                 if (a->data[i]>b->data[i])return (sign==1?-1:1);
                 else if (a->data[i]<b->data[i]) return (sign==1?1:-1);
            }
            return 0;
        }
}

bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b)
{
    // 情况一
    if(a->sign==0 && b->sign==0) {
        if (cmp(a,b)>=0) { // 被减数大于等于减数,即差大于等于0时
                int len = a->len;
                bignumber_s *result = make_bignumber_temp(len,0);
                sub_impl(a,b,result);
                return result;
        } else { // 被减数小于减数,即差小于0时
                int len = b->len;
                bignumber_s *result = make_bignumber_temp(len,1);
                sub_impl(b,a,result);
                return result;
        }
    } else if (a->sign==1 && b->sign==1) { //情况四
        b->sign=0;  
        a->sign=0;
        return calc_sub(b,a); //调换减数和被减数,变成情况一
    }else if (a->sign==0 && b->sign==1) { // 情况二
        b->sign=0;
        bignumber_s *result = calc_add(a,b);
        result->sign = 0;
        return result; 
    } else if (a->sign==1 && b->sign==0) { // 情况三
        a->sign=0;
        bignumber_s *result = calc_add(a,b);
        result->sign = 1;
        return result; 
    } 
}

bignumber_s *calc_mul(bignumber_s *a, bignumber_s *b)
{
  // 实现乘法
  return NULL;
}
bignumber_s *calc_div(bignumber_s *a, bignumber_s *b)
{
  // 实现除法
  return NULL;
}
int main(int argc, char *argv[])
{
    bignumber_s *a = make_bignumber_fromstr(argv[1]);
    bignumber_s *b = make_bignumber_fromstr(argv[3]);
    if (argc!=4) usage(argv[0]);

    if (0 == strcmp(argv[2],"+"))
        print_bignumber(calc_add(a,b));
    else if (0 == strcmp(argv[2],"-"))
        print_bignumber(calc_sub(a,b));
    else if (0 == strcmp(argv[2],"x"))
        print_bignumber(calc_mul(a,b));
    else if (0 == strcmp(argv[2],"/"))
        print_bignumber(calc_div(a,b));
    else usage(argv[0]);
    return 0;
}
2.4.4 演示

编译命令:

$ gcc -o calculatorv2 calculatorv2.c

结果演示:

此处输入图片的描述

2.5 实现乘法

2.5.1 正数乘法实现

乘法的实现稍微比加减法复杂一些,但是和加减法是同样的实现原理。

依旧搬出我们的竖式计算法:

此处输入图片的描述

假设x[0...n]乘以y[0...m],其中0代表x数的个位,n代表x数的最高位,假设用x代表上述的145这个数。那么x[0]就是5, x[1]就是4, x[1]就是1; 类似的y代表12, 则y[0]为2, y[1]为1

乘法的实现思路:

  1. x与y的积,位数为n+m,即积为z[0...n+m]
  2. 先用y的每个位去乘以x,如上述例子先用2乘以145,再用1乘以145, 从而得出对应于y每个位的积。
  3. 再将步骤2计算出来的所有积,按其基数累加到积中。

这里有两点需要注意:

  1. y[i]乘以x[j]时,其积应在z[i+j]。
  2. y[i]乘以x[0...n]其积,应累加到z[i...m+n]中

代码实现如下:

void mul_impl(bignumber_s *x, bignumber_s *y, bignumber_s *z)
{
    int n = x->len;
    int m = y->len;
    int i = 0;
    int j = 0;
    int carry = 0;
    for (i=0; i<m; i++) {
        // y的每一位乘以x,即计算y[i] * x[0...n] 并累加到z[i...i+n]中
        for (j=0; j<n; j++) {
            carry += y->data[i]*x->data[j] + z->data[i+j];
            z->data[i+j] = carry%BASE;
            carry /= BASE;
        }

        // 将剩余的进位,继续在z[i+n...n+m]中累加,从而完成y[i]乘以x[0...n]其积累加到z[i...m+n]中
        for (; j+i<n+m; j++) {
            carry += z->data[i+j];
            z->data[i+j] = carry % BASE;
            carry /= BASE;
        }
    }
}

bignumber_s *calc_mul(bignumber_s *a, bignumber_s *b)
{
    int len = a->len + b->len;
    bignumber_s *result = make_bignumber_temp(len,0);
    mul_impl(a,b,result);
    return result;
}
2.5.2 有符号乘法

有符号的 乘法可以转化为无符号的乘法,分以下两种情况:

// 情况一 (A>0 && B>0) 或者 (A>0 && B>0)
A x B --> |A| x |B|
// 情况二 (A>0 && B<0) 或者 (A<0 && B>0)
A x B --> -(|A| x |B|)

代码实现如下:

bignumber_s *calc_mul(bignumber_s *a, bignumber_s *b)
{
    int len = a->len + b->len;
    bignumber_s *result = make_bignumber_temp(len,a->sign==b->sign?0:1); //a->sign==b->sign为情况一,否则为情况二。
    mul_impl(a,b,result);
    return result;
}
2.5.3大数计算器v3版本
/*
* file name : calculatorv3.c
* desp : calculator version 3
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BASE (10)
#define MAX(x,y) ((x)>(y)?(x):(y))

// 大数数据结构
typedef struct bignumber_s{
    char sign; // 代表大数的符号,1为负数,0为正数
    int len;   // 代表大数的位数
    char data[]; // 大数的数据内容,data[0]代表个位,data[1]代表十位,data[2]代表百位....
} bignumber_s;

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b);
bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b);

// 构造大数模板,len指向大数的数据位数,sign表示该大数的符号
bignumber_s *make_bignumber_temp(int len, int sign)
{
    // 分配bignumber_s及其所代表数据 所需的内存
    bignumber_s *temp = malloc(sizeof(bignumber_s)+len);
    if (NULL == temp) {
        perror("Malloc");
        exit(-1);
    }
    temp->sign = sign;
    temp->len  = len;
    memset(temp->data, 0, len);
    return temp;
}

// 处理数字字符串冗余的0,即"00123" -> "123"
const char *strip_str(const char *str)
{
    int i = 0;
        int len = strlen(str);
        for (i=0; i<len-1&&str[i]=='0'; i++) ;
        return str+i;
}
// 将字符串数据填充进模板中
void fill_data_fromstr(bignumber_s *n, const char *str)
{
    int i = 0;
    int len = n->len;
    for (i=0; i<len; i++) {
        int d = str[len-1-i]-'0';
        if (d>=0 && d<=9) 
            n->data[i] = d;
        else {
          fprintf(stderr, "Invalid Number:%s\n", str);
          exit(-1);
        }
    }
}
// 从字符串构造一个大数
bignumber_s *make_bignumber_fromstr(const char *str)
{
    // 处理负数字符串,即"-123"
    int sign = 0;
    if (str[0]=='-') {
        sign = 1;
        str++;
    }
    // 处理数字字符串冗余的0,即"00123" -> "123"
    const char * striped_str = strip_str(str);

    int len = strlen(striped_str);
    // 指定数据位长度即符号,创建一个大数模板
    bignumber_s *temp = make_bignumber_temp(len ,sign);
    // 将字符串数据填充进模板中,完成大数创建
    fill_data_fromstr(temp, striped_str);
    return temp;
}

// 以字符串的形式打印输出一个大数
void print_bignumber(bignumber_s* b)
{
   int len = b->len;
   char *str = malloc(len+1);
   int i = 0;

   for (i=0; i<len; i++) {
      str[i] = b->data[len-i-1]+'0';
   }
   str[len] = '\0';

   fprintf(stdout,"%s%s\n", b->sign==1?"-":"", strip_str(str));
   free(str);
}

void usage(const char *s)
{
    fprintf(stderr, "Usage:%s number1 +-x/ number2.\n",s);
    exit(-1);
}

// 实现无符号加法,a和b为加数,r为和
void add_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
    int i = 0;
    char carry = 0;
    int len = r->len;
    for (i=0; i<len; i++) {
       if (i<a->len)carry += a->data[i];
       if (i<b->len)carry += b->data[i];
       r->data[i] = carry%BASE;
       carry /= BASE;
    }
}

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b)
{
    // 情况一和情况四
    if (a->sign==b->sign) {
        // n位数+m位数,其和最大为max(n,m)+1位数
        int len = MAX(a->len, b->len) + 1;
        bignumber_s *result = make_bignumber_temp(len,a->sign);

        add_impl(a,b,result);
        return result;
    } else if (a->sign == 0 && b->sign == 1) {//情况二
        b->sign = 0; //b数去绝对值
        return calc_sub(a,b);
    } else if (a->sign == 1 && b->sign == 0) {//情况三
        a->sign = 0; //a数去绝对值
        return calc_sub(b,a);
    }
}

// 实现无符号减法,a-b , r为差
void sub_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
        int i=0;
        int borrow = 0;
        int len = r->len;
        int temp = 0;
        for (i=0; i<len; i++) {
            temp = a->data[i]+BASE-borrow-((i<b->len)?b->data[i]:0);
            r->data[i] = temp%BASE;
            borrow = temp/BASE?0:1;
        }
}

int valid_len(bignumber_s *a)
{
    int len = a->len;
    int i = len-1;
    for (i=len-1; i>=0; i--) {
        if (a->data[i]==0) len--;
        else break;
    }
    return len;
}
// 判断两个大数的大小
// a > b 返回1 ,a<b 返回 -1 , a==b 返回0
int cmp(bignumber_s *a, bignumber_s *b)
{

        if (a->sign==0&&b->sign==1) return 1;
        if (a->sign==1&&b->sign==0) return -1;

        int sign = a->sign;
        int alen = valid_len(a);
        int blen = valid_len(b);
        if (alen>blen) return (sign==1?-1:1);
        else if (alen<blen) return (sign==1?1:-1);
        else {
                int i = 0;
                int len = alen;
                for (i=len-1; i>=0; i--) {
                 if (a->data[i]>b->data[i])return (sign==1?-1:1);
                 else if (a->data[i]<b->data[i]) return (sign==1?1:-1);
            }
            return 0;
        }
}

bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b)
{
    // 情况一
    if(a->sign==0 && b->sign==0) {
        if (cmp(a,b)>=0) { // 被减数大于等于减数,即差大于等于0时
                int len = a->len;
                bignumber_s *result = make_bignumber_temp(len,0);
                sub_impl(a,b,result);
                return result;
        } else { // 被减数小于减数,即差小于0时
                int len = b->len;
                bignumber_s *result = make_bignumber_temp(len,1);
                sub_impl(b,a,result);
                return result;
        }
    } else if (a->sign==1 && b->sign==1) { //情况四
        b->sign=0;  
        a->sign=0;
        return calc_sub(b,a); //调换减数和被减数,变成情况一
    }else if (a->sign==0 && b->sign==1) { // 情况二
        b->sign=0;
        bignumber_s *result = calc_add(a,b);
        result->sign = 0;
        return result; 
    } else if (a->sign==1 && b->sign==0) { // 情况三
        a->sign=0;
        bignumber_s *result = calc_add(a,b);
        result->sign = 1;
        return result; 
    } 
}

// 实现无符号乘法,axb , z为积
void mul_impl(bignumber_s *x, bignumber_s *y, bignumber_s *z)
{
    int n = x->len;
    int m = y->len;
    int i = 0;
    int j = 0;
    int carry = 0;
    for (i=0; i<m; i++) {
        // y的每一位乘以x,即计算y[i] * x[0...n] 并累加到z[i...i+n]中
        for (j=0; j<n; j++) {
            carry += y->data[i]*x->data[j] + z->data[i+j];
            z->data[i+j] = carry%BASE;
            carry /= BASE;
        }

        // 将剩余的进位,继续在z[i+n...n+m]中累加,从而完成y[i]乘以x[0...n]其积累加到z[i...m+n]中
        for (; j+i<n+m; j++) {
            carry += z->data[i+j];
            z->data[i+j] = carry % BASE;
            carry /= BASE;
        }
    }
}

bignumber_s *calc_mul(bignumber_s *a, bignumber_s *b)
{
    int len = a->len + b->len;
    bignumber_s *result = make_bignumber_temp(len,a->sign==b->sign?0:1); //a->sign==b->sign为情况一,否则为情况二。
    mul_impl(a,b,result);
    return result;
}

bignumber_s *calc_div(bignumber_s *a, bignumber_s *b)
{
  // 实现除法
  return NULL;
}
int main(int argc, char *argv[])
{
    bignumber_s *a = make_bignumber_fromstr(argv[1]);
    bignumber_s *b = make_bignumber_fromstr(argv[3]);
    if (argc!=4) usage(argv[0]);

    if (0 == strcmp(argv[2],"+"))
        print_bignumber(calc_add(a,b));
    else if (0 == strcmp(argv[2],"-"))
        print_bignumber(calc_sub(a,b));
    else if (0 == strcmp(argv[2],"x"))
        print_bignumber(calc_mul(a,b));
    else if (0 == strcmp(argv[2],"/"))
        print_bignumber(calc_div(a,b));
    else usage(argv[0]);
    return 0;
}
2.5.4 演示

编译命令:

$ gcc -o calculatorv3 calculatorv3.c

结果演示:

此处输入图片的描述

2.6 实现除法

2.6.1 除法实现

除法的实现,我们直接转化为减法,大致思路如下:

  1. 用被除数减去除数,
  2. 差大于等于0时,商的值加1,将差赋值给被除数,重新回到步骤1
  3. 差小于0时,整个除法计算过程完毕。

这种实现方法,在某种情况下效率会非常低,但是实现起来比较简单。

有符号的除法,其处理更乘法是一致的。

除法实现代码如下:

// 大数加一操作
void plusone(bignumber_s *a)
{
   int len = a->len ;
   int i;
   int carry = 1;
   for (i=0; i<len; i++) {
      carry += a->data[i];
      a->data[i] = carry%BASE;
      carry /= BASE;
   }
}
// 大数除法实现
bignumber_s *calc_div(bignumber_s *a, bignumber_s *b)
{
    bignumber_s *zero = make_bignumber_temp(1,0);
    if (cmp(b,zero)==0) {// 除数为0 报错
        fprintf(stderr,"Integer division by zero\n");
        exit(-1);
    }else if (cmp(a,zero)==0) { // 被除数为0 ,结果为0
        return zero;
    }

    int len = a->len;
    bignumber_s *result = make_bignumber_temp(len,a->sign==b->sign?0:1);
    a->sign = 0;
    b->sign = 0;
    bignumber_s *temp = make_bignumber_temp(len, 0);
    bignumber_s *aa = a;
    while(1) {
         if (cmp(aa, b)>=0) {
             sub_impl(aa, b, temp);
             plusone(result);
             aa = temp;
         } else {
             free(temp);
             return result;
         }
    }
}
2.6.2 大数计算器v4版本
/*
* file name : calculatorv4.c
* desp : calculator version 4
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BASE (10)
#define MAX(x,y) ((x)>(y)?(x):(y))

// 大数数据结构
typedef struct bignumber_s{
    char sign; // 代表大数的符号,1为负数,0为正数
    int len;   // 代表大数的位数
    char data[]; // 大数的数据内容,data[0]代表个位,data[1]代表十位,data[2]代表百位....
} bignumber_s;

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b);
bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b);

// 构造大数模板,len指向大数的数据位数,sign表示该大数的符号
bignumber_s *make_bignumber_temp(int len, int sign)
{
    // 分配bignumber_s及其所代表数据 所需的内存
    bignumber_s *temp = malloc(sizeof(bignumber_s)+len);
    if (NULL == temp) {
        perror("Malloc");
        exit(-1);
    }
    temp->sign = sign;
    temp->len  = len;
    memset(temp->data, 0, len);
    return temp;
}

// 处理数字字符串冗余的0,即"00123" -> "123"
const char *strip_str(const char *str)
{
    int i = 0;
        int len = strlen(str);
        for (i=0; i<len-1&&str[i]=='0'; i++) ;
        return str+i;
}
// 将字符串数据填充进模板中
void fill_data_fromstr(bignumber_s *n, const char *str)
{
    int i = 0;
    int len = n->len;
    for (i=0; i<len; i++) {
        int d = str[len-1-i]-'0';
        if (d>=0 && d<=9) 
            n->data[i] = d;
        else {
          fprintf(stderr, "Invalid Number:%s\n", str);
          exit(-1);
        }
    }
}
// 从字符串构造一个大数
bignumber_s *make_bignumber_fromstr(const char *str)
{
    // 处理负数字符串,即"-123"
    int sign = 0;
    if (str[0]=='-') {
        sign = 1;
        str++;
    }
    // 处理数字字符串冗余的0,即"00123" -> "123"
    const char * striped_str = strip_str(str);

    int len = strlen(striped_str);
    // 指定数据位长度即符号,创建一个大数模板
    bignumber_s *temp = make_bignumber_temp(len ,sign);
    // 将字符串数据填充进模板中,完成大数创建
    fill_data_fromstr(temp, striped_str);
    return temp;
}

// 以字符串的形式打印输出一个大数
void print_bignumber(bignumber_s* b)
{
   int len = b->len;
   char *str = malloc(len+1);
   int i = 0;

   for (i=0; i<len; i++) {
      str[i] = b->data[len-i-1]+'0';
   }
   str[len] = '\0';

   fprintf(stdout,"%s%s\n", b->sign==1?"-":"", strip_str(str));
   free(str);
}

void usage(const char *s)
{
    fprintf(stderr, "Usage:%s number1 +-x/ number2.\n",s);
    exit(-1);
}

// 实现无符号加法,a和b为加数,r为和
void add_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
    int i = 0;
    char carry = 0;
    int len = r->len;
    for (i=0; i<len; i++) {
       if (i<a->len)carry += a->data[i];
       if (i<b->len)carry += b->data[i];
       r->data[i] = carry%BASE;
       carry /= BASE;
    }
}

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b)
{
    // 情况一和情况四
    if (a->sign==b->sign) {
        // n位数+m位数,其和最大为max(n,m)+1位数
        int len = MAX(a->len, b->len) + 1;
        bignumber_s *result = make_bignumber_temp(len,a->sign);

        add_impl(a,b,result);
        return result;
    } else if (a->sign == 0 && b->sign == 1) {//情况二
        b->sign = 0; //b数去绝对值
        return calc_sub(a,b);
    } else if (a->sign == 1 && b->sign == 0) {//情况三
        a->sign = 0; //a数去绝对值
        return calc_sub(b,a);
    }
}

// 实现无符号减法,a-b , r为差
void sub_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
        int i=0;
        int borrow = 0;
        int len = r->len;
        int temp = 0;
        for (i=0; i<len; i++) {
            temp = a->data[i]+BASE-borrow-((i<b->len)?b->data[i]:0);
            r->data[i] = temp%BASE;
            borrow = temp/BASE?0:1;
        }
}

int valid_len(bignumber_s *a)
{
    int len = a->len;
    int i = len-1;
    for (i=len-1; i>=0; i--) {
        if (a->data[i]==0) len--;
        else break;
    }
    return len;
}
// 判断两个大数的大小
// a > b 返回1 ,a<b 返回 -1 , a==b 返回0
int cmp(bignumber_s *a, bignumber_s *b)
{

        if (a->sign==0&&b->sign==1) return 1;
        if (a->sign==1&&b->sign==0) return -1;

        int sign = a->sign;
        int alen = valid_len(a);
        int blen = valid_len(b);
        if (alen>blen) return (sign==1?-1:1);
        else if (alen<blen) return (sign==1?1:-1);
        else {
                int i = 0;
                int len = alen;
                for (i=len-1; i>=0; i--) {
                 if (a->data[i]>b->data[i])return (sign==1?-1:1);
                 else if (a->data[i]<b->data[i]) return (sign==1?1:-1);
            }
            return 0;
        }
}

bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b)
{
    // 情况一
    if(a->sign==0 && b->sign==0) {
        if (cmp(a,b)>=0) { // 被减数大于等于减数,即差大于等于0时
                int len = a->len;
                bignumber_s *result = make_bignumber_temp(len,0);
                sub_impl(a,b,result);
                return result;
        } else { // 被减数小于减数,即差小于0时
                int len = b->len;
                bignumber_s *result = make_bignumber_temp(len,1);
                sub_impl(b,a,result);
                return result;
        }
    } else if (a->sign==1 && b->sign==1) { //情况四
        b->sign=0;  
        a->sign=0;
        return calc_sub(b,a); //调换减数和被减数,变成情况一
    }else if (a->sign==0 && b->sign==1) { // 情况二
        b->sign=0;
        bignumber_s *result = calc_add(a,b);
        result->sign = 0;
        return result; 
    } else if (a->sign==1 && b->sign==0) { // 情况三
        a->sign=0;
        bignumber_s *result = calc_add(a,b);
        result->sign = 1;
        return result; 
    } 
}

// 实现无符号乘法,x * y , z为积
void mul_impl(bignumber_s *x, bignumber_s *y, bignumber_s *z)
{
    int n = x->len;
    int m = y->len;
    int i = 0;
    int j = 0;
    int carry = 0;
    for (i=0; i<m; i++) {
        // y的每一位乘以x,即计算y[i] * x[0...n] 并累加到z[i...i+n]中
        for (j=0; j<n; j++) {
            carry += y->data[i]*x->data[j] + z->data[i+j];
            z->data[i+j] = carry%BASE;
            carry /= BASE;
        }

        // 将剩余的进位,继续在z[i+n...n+m]中累加,从而完成y[i]乘以x[0...n]其积累加到z[i...m+n]中
        for (; j+i<n+m; j++) {
            carry += z->data[i+j];
            z->data[i+j] = carry % BASE;
            carry /= BASE;
        }
    }
}

bignumber_s *calc_mul(bignumber_s *a, bignumber_s *b)
{
    int len = a->len + b->len;
    bignumber_s *result = make_bignumber_temp(len,a->sign==b->sign?0:1); //a->sign==b->sign为情况一,否则为情况二。
    mul_impl(a,b,result);
    return result;
}

// 大数加一操作
void plusone(bignumber_s *a)
{
   int len = a->len ;
   int i;
   int carry = 1;
   for (i=0; i<len; i++) {
      carry += a->data[i];
      a->data[i] = carry%BASE;
      carry /= BASE;
   }
}
// 大数除法实现
bignumber_s *calc_div(bignumber_s *a, bignumber_s *b)
{
    bignumber_s *zero = make_bignumber_temp(1,0);
    if (cmp(b,zero)==0) {// 除数为0 报错
        fprintf(stderr,"Integer division by zero\n");
        exit(-1);
    }else if (cmp(a,zero)==0) { // 被除数为0 ,结果为0
        return zero;
    }

    int len = a->len;
    bignumber_s *result = make_bignumber_temp(len,a->sign==b->sign?0:1);
    a->sign = 0;
    b->sign = 0;
    bignumber_s *temp = make_bignumber_temp(len, 0);
    bignumber_s *aa = a;
    while(1) {
         if (cmp(aa, b)>=0) {
             sub_impl(aa, b, temp);
             plusone(result);
             aa = temp;
         } else {
             free(temp);
             return result;
         }
    }
}

int main(int argc, char *argv[])
{
    bignumber_s *a = make_bignumber_fromstr(argv[1]);
    bignumber_s *b = make_bignumber_fromstr(argv[3]);
    if (argc!=4) usage(argv[0]);

    if (0 == strcmp(argv[2],"+"))
        print_bignumber(calc_add(a,b));
    else if (0 == strcmp(argv[2],"-"))
        print_bignumber(calc_sub(a,b));
    else if (0 == strcmp(argv[2],"x"))
        print_bignumber(calc_mul(a,b));
    else if (0 == strcmp(argv[2],"/"))
        print_bignumber(calc_div(a,b));
    else usage(argv[0]);
    return 0;
}
2.6.3 演示

编译命令:

$ gcc -o calculatorv4 calculatorv4.c

结果演示:

此处输入图片的描述

大数计算器在解决除法问题的时候仅实现了商的求解,并未对余数进行过多的处理,其实这是不完善的。大家可以在此基础上,思考思考如何改进能够让计算器更加完善。

三、实验总结

本实验通过完成一个大数计算器的玩具程序,一方面有助于理解计算机数学运算的实现机理,另一方面有助于提升c语言的编程能力。 当然我们的大数计算器,在除法方面的性能还是不太令人满意的,希望有兴趣的读者朋友,后续能够对计算器实现进行改进。

四、参考链接

  • 5
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的用C语言实现计算器软件的代码,包括了大数相加相减、进制转换、括号匹配和表达式求值等功能: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define MAX_SIZE 1000 // 大数结构体 typedef struct { int digits[MAX_SIZE]; int len; } BigNumber; // 初始化大数 void init(BigNumber *n) { memset(n->digits, 0, sizeof(n->digits)); n->len = 0; } // 将字符串转换为大数 void str2BigNumber(char *str, BigNumber *n) { init(n); int len = strlen(str); for (int i = 0; i < len; i++) { n->digits[i] = str[len - i - 1] - '0'; } n->len = len; } // 将大数转换为字符串 void bigNumber2Str(BigNumber *n, char *str) { int i = 0; for (int j = n->len - 1; j >= 0; j--) { str[i++] = n->digits[j] + '0'; } str[i] = '\0'; } // 判断两个大数的大小关系 int compare(BigNumber *a, BigNumber *b) { if (a->len < b->len) return -1; if (a->len > b->len) return 1; for (int i = a->len - 1; i >= 0; i--) { if (a->digits[i] < b->digits[i]) return -1; if (a->digits[i] > b->digits[i]) return 1; } return 0; } // 大数相加 void add(BigNumber *a, BigNumber *b, BigNumber *c) { init(c); int carry = 0; for (int i = 0; i < a->len || i < b->len; i++) { int sum = a->digits[i] + b->digits[i] + carry; c->digits[c->len++] = sum % 10; carry = sum / 10; } if (carry) { c->digits[c->len++] = carry; } } // 大数相减 void subtract(BigNumber *a, BigNumber *b, BigNumber *c) { init(c); if (compare(a, b) < 0) { fprintf(stderr, "Error: a < b in subtraction.\n"); exit(1); } int borrow = 0; for (int i = 0; i < a->len; i++) { int sub = a->digits[i] - borrow; if (i < b->len) sub -= b->digits[i]; if (sub < 0) { sub += 10; borrow = 1; } else { borrow = 0; } c->digits[c->len++] = sub; } while (c->len > 1 && c->digits[c->len - 1] == 0) { c->len--; } } // 2进制转换 void bin2Dec(char *bin, char *dec) { BigNumber n, base, result; str2BigNumber("1", &base); init(&result); int len = strlen(bin); for (int i = 0; i < len; i++) { if (bin[i] == '1') { str2BigNumber("2", &n); for (int j = 0; j < len - i - 1; j++) { multiply(&n, &base, &n); } add(&result, &n, &result); } multiply(&base, &base, &base); } bigNumber2Str(&result, dec); } // 8进制转换 void oct2Dec(char *oct, char *dec) { BigNumber n, base, result; str2BigNumber("1", &base); init(&result); int len = strlen(oct); for (int i = 0; i < len; i++) { str2BigNumber(&oct[i], &n); for (int j = 0; j < len - i - 1; j++) { multiply(&n, &base, &n); } add(&result, &n, &result); } bigNumber2Str(&result, dec); } // 16进制转换 void hex2Dec(char *hex, char *dec) { BigNumber n, base, result; str2BigNumber("1", &base); init(&result); int len = strlen(hex); for (int i = 0; i < len; i++) { int digit = 0; if (hex[i] >= '0' && hex[i] <= '9') { digit = hex[i] - '0'; } else if (hex[i] >= 'A' && hex[i] <= 'F') { digit = hex[i] - 'A' + 10; } else if (hex[i] >= 'a' && hex[i] <= 'f') { digit = hex[i] - 'a' + 10; } else { fprintf(stderr, "Error: invalid hex digit.\n"); exit(1); } str2BigNumber(&hex[i], &n); for (int j = 0; j < len - i - 1; j++) { multiply(&n, &base, &n); } add(&result, &n, &result); } bigNumber2Str(&result, dec); } // 十进制转换为2进制 void dec2Bin(char *dec, char *bin) { BigNumber n, base, result; str2BigNumber(dec, &n); str2BigNumber("2", &base); init(&result); while (n.len > 1 || n.digits[0] > 0) { divide(&n, &base, &n, &result); bin[strlen(bin)] = result.digits[0] + '0'; } for (int i = 0; i < strlen(bin) / 2; i++) { char temp = bin[i]; bin[i] = bin[strlen(bin) - i - 1]; bin[strlen(bin) - i - 1] = temp; } } // 十进制转换为8进制 void dec2Oct(char *dec, char *oct) { BigNumber n, base, result; str2BigNumber(dec, &n); str2BigNumber("8", &base); init(&result); while (n.len > 1 || n.digits[0] > 0) { divide(&n, &base, &n, &result); oct[strlen(oct)] = result.digits[0] + '0'; } for (int i = 0; i < strlen(oct) / 2; i++) { char temp = oct[i]; oct[i] = oct[strlen(oct) - i - 1]; oct[strlen(oct) - i - 1] = temp; } } // 十进制转换为16进制 void dec2Hex(char *dec, char *hex) { BigNumber n, base, result; str2BigNumber(dec, &n); str2BigNumber("16", &base); init(&result); while (n.len > 1 || n.digits[0] > 0) { divide(&n, &base, &n, &result); if (result.digits[0] < 10) { hex[strlen(hex)] = result.digits[0] + '0'; } else { hex[strlen(hex)] = result.digits[0] - 10 + 'A'; } } for (int i = 0; i < strlen(hex) / 2; i++) { char temp = hex[i]; hex[i] = hex[strlen(hex) - i - 1]; hex[strlen(hex) - i - 1] = temp; } } // 判断括号是否匹配 int match(char *exp) { char stack[MAX_SIZE]; int top = -1; int len = strlen(exp); for (int i = 0; i < len; i++) { if (exp[i] == '(') { stack[++top] = exp[i]; } else if (exp[i] == ')') { if (top < 0) { return 0; } else { top--; } } } return top == -1; } // 判断运算符的优先级 int precedence(char op) { if (op == '+' || op == '-') { return 1; } if (op == '*' || op == '/') { return 2; } return 0; } // 计算表达式的值 void evaluate(char *exp, char *res) { char stack[MAX_SIZE]; int top = -1; int len = strlen(exp); for (int i = 0; i < len; i++) { if (isdigit(exp[i])) { int num = 0; while (i < len && isdigit(exp[i])) { num = num * 10 + (exp[i] - '0'); i++; } i--; sprintf(&stack[++top], "%d", num); } else if (exp[i] == '(') { stack[++top] = exp[i]; } else if (exp[i] == ')') { while (top >= 0 && stack[top] != '(') { char op = stack[top--]; char b[MAX_SIZE], a[MAX_SIZE]; strcpy(b, stack[top--]); strcpy(a, stack[top--]); BigNumber x, y, z; str2BigNumber(a, &x); str2BigNumber(b, &y); if (op == '+') { add(&x, &y, &z); } else if (op == '-') { subtract(&x, &y, &z); } else if (op == '*') { multiply(&x, &y, &z); } else if (op == '/') { divide(&x, &y, &z, NULL); } bigNumber2Str(&z, stack[++top]); } top--; } else { while (top >= 0 && precedence(stack[top]) >= precedence(exp[i])) { char op = stack[top--]; char b[MAX_SIZE], a[MAX_SIZE]; strcpy(b, stack[top--]); strcpy(a, stack[top--]); BigNumber x, y, z; str2BigNumber(a, &x); str2BigNumber(b, &y); if (op == '+') { add(&x, &y, &z); } else if (op == '-') { subtract(&x, &y, &z); } else if (op == '*') { multiply(&x, &y, &z); } else if (op == '/') { divide(&x, &y, &z, NULL); } bigNumber2Str(&z, stack[++top]); } stack[++top] = exp[i]; } } while (top > 0) { char op = stack[top--]; char b[MAX_SIZE], a[MAX_SIZE]; strcpy(b, stack[top--]); strcpy(a, stack[top--]); BigNumber x, y, z; str2BigNumber(a, &x); str2BigNumber(b, &y); if (op == '+') { add(&x, &y, &z); } else if (op == '-') { subtract(&x, &y, &z); } else if (op == '*') { multiply(&x, &y, &z); } else if (op == '/') { divide(&x, &y, &z, NULL); } bigNumber2Str(&z, stack[++top]); } strcpy(res, stack[0]); } int main() { char exp[MAX_SIZE], res[MAX_SIZE]; printf("Enter an expression: "); scanf("%s", exp); if (match(exp)) { evaluate(exp, res); printf("Result: %s\n", res); } else { printf("Error: mismatched parentheses.\n"); } return 0; } ``` 这段代码实现大数相加相减、进制转换、括号匹配和表达式求值等功能。可以通过输入不同的命令来调用不同的函数,实现不同的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值