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 0x0int 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; }