C语言学习趣事_大数运算_之加法
1、引子
在C语言中,因为预定义的自然数类型的大小是有上下限度的,这就决定了在进行数的运算的时候,必然受到限制,同时因为C语言是最接近汇编的一种程序设计语言,并且由于计算机的运算的特殊性: 按位进行计算的,这样还带来了一个问题,存在数的丢失,就是通常说的溢出。
为了在C中进行大数运算,不能简单的用C中预定义的数据类型和运算方法进行,因此必须寻求一种新的方法。本文讨论怎样来设计一种方法来计算各种大数运算。
2、加法
我们知道,实际上计算机通过位运算来实现数学运算,我们通过一个简单的例子来说明这个问题。
Exp:
100 ————> 0110_0100 (为了简单的表示运算我们假设运算量是占用一个Byte的)
50 ————> 0011_0010
+ +
————————————————————
150 1001_0110
通过上面的例子我们可以看到,其实我们就可以得到启发,我们在进行数学运算的时候,是否也可以“按位”进行处理呢? 是否可行,我现在也不知道,但是这给了我们一个思路。
3、大数加法的思路
在进行加法的时候,我们不需要关注本身数字的大小,而只需要关心组成数字的每一个数学符号就行, 例如我们有两个数, 13和12, 我们不必要关心13表示距离0的距离为13这个基本的意义, 我们只需要关注这里有两个数符占据了个位、十位就行,同样12一样处理,当我们进行数学运算的时候我们就按照每个位进行计算就行,
Exp:
13 ————> 1 3
12 ————> 1 2
+ +
————————————————————
25 2 5
我们最终将: 2和5 分别放到对应的基位占位符上就行, 把2 放到十位, 把5放到个位,结果可以得到: 25。对于更多或者任意位的数学加法运算我们都可以按照这样的思路处理。
4、设计数据结构
我们知道在计算机世界里面有一个经典的说法,我忘记是否是冯若-依曼说的了,那就是:
程序=数据结构+算法
为了实现这样的运算我们需要进行设计一个特殊的数据结构,很显然用结构体比较合适,因为我们不知道到底会输入多少位的数字,因此为了实现任意位的数学运算我们需要用到基本的数据组织形态,就是链表。(在计算机存储能力最大限度之内的运算均可通过这种方法实现)。
定义数据结构:
Exp:
typedef struct Node_Sum
{
char chInput; //用来存储每一位的输入字符
unsigned int cFlag; //用来表示每一位相加是否有进位
struct Node_Sum *Next; //用来指向输入的下一个节点
} NODESUM;
5、申请存储空间
既然没有办法预定义用户的输入长度,那么我们只能是动态申请内存了。
我们可以用下面的函数实现动态内存申请:
Exp:
NODESUM *get_memory(void)
{
return (NODESUM *)malloc(sizeof(NODESUM));
}
6、保存用户输入
可以利用循环读入字符的原则来实现用户的输入;
char chInput;
NODESUM *nodesum;
while((chInput=getchar()) != ' '|| chInput != 0x0D )
{
nodesum->next=get_memory();
nodesum->chInput=chInput;
}
7、转换数据的
因为我们输入的数字是按照高进制位从左到有输入的,这里我们输入也是由左往右输入的,正好与数字相对称,但是这里会引起一个问题,那就是当我们
输入的加数和被加数的位数不一致的情况,将会影响到我们两个数的相加运算。这里有三个方法来处理这种情况:
1、是将两个链表都倒置一下,然后进行相加,相加后再将结果倒置输出
2、就是将短的那个进行从左开始补零,直到两个数的位数一致。
3、就是将输入的字符串进行右对齐,然后进行操作。
Exp:
123456789
000000012
+
————————————
从算法的简单性和运算的时间上来看,我们知道用第二种方法更加简单。
8、计算
最后一步进行计算,这个就简单了,我们可以用字符进行计算,也没有必要转换成十进制的数据进行计算。这里需要用到一点关于ASCII码的知识,需要
一张ASCII码表进行对照。
查表知道:
字符0 ——> 0x30
字符9 ——> 0x39
因此如果两个字符相加大于0x69就表示相加的位有进位,我们设置我们的进位标志位为1;否则就不置位进位标志位,使其为0;当进行下一个计算的时候我们将进位标志
位同时将进位标志位也进行相加,加完后清楚进位标志位就可以实现我们的整个相加的过程。
9、实例代码
这里我们设计一个最多可以实现100位数的加法运算程序,如果更多的数据位,我们就不讨论了。留待有兴趣的进行讨论。这里给出一个简单数组实现的代码,如果将代
码进行修改就可以实现想要的数据长度的计算。
Exp:
/* * 程序实例演示100位大数加法的运算 * */ #include <stdio.h> #include <ctype.h> #define LENGTH 100 #define NUL 0x0 int main(int argc,char **argv) { char aug_end[LENGTH], //加数 add_end[LENGTH], //被加数 sum[100]; int cFlags, //进位标志位 temp, i, j, i_aug_len, //加数的位数 i_add_len, //被加数的位数 ch_2_i_aug, ch_2_i_add; //初始化数组 i=0; while(i<100) { aug_end[i]=NUL; add_end[i]=NUL; sum[i]=NUL; i++; } //输入加数 i=0; i_aug_len=0; printf("Please input the augend:"); while(isdigit(temp=getc(stdin))) { aug_end[i]=temp; i++; i_aug_len++; if(i>=100) break; } fflush(stdin); //数据对齐,将输入数据右对齐 for(i=100-i_aug_len;i>0;i--) for(j=100-i;j>=0;j--) { aug_end[j+1]=aug_end[j]; } i=100-i_aug_len; while(i>=0) { aug_end[i-1]=0x30; i--; } //输入被加数 i=0; i_add_len=0; printf("Please input the augend:"); while(isdigit(temp=getc(stdin))) { add_end[i]=temp; i++; i_add_len++; if(i>=100) break; } fflush(stdin); //数据对齐,将输入数据右对齐 for(i=100-i_add_len;i>0;i--) for(j=100-i;j>=0;j--) { add_end[j+1]=add_end[j]; } i=100-i_add_len; while(i>=0) { add_end[i-1]=0x30; i--; } //进行加法运算,按照位置进行计算,从右向左运算 cFlags=0; //首次运算置位进位标志位为0 for(i=99;i>=0;i--) { ch_2_i_aug=aug_end[i]-48; // char ——》 int ch_2_i_add=add_end[i]-48; temp=ch_2_i_aug+ch_2_i_add+cFlags; if(temp >=10) { //如果进位了,就应该将个位数取出来,同时将进位标志位置1 cFlags=1; temp-=10; } else { cFlags=0; } sum[i]=temp+48; // int ——》 char } //输出运算结果 puts("\nThe sum of the augadd and addend is:"); if(cFlags==1) putc('1',stdout); for(i=0;i<100;i++) { if(isdigit(sum[i])) putc(sum[i],stdout); } putc('\n',stdout); return 0; }