爲什麼需要高精度計算
對於 C++ 而言,最大的數據爲 long long(64b,8位),對於超過 8B 的數據,C++ 沒有對應的數據類型進行表示。所以我們需要知道高精度計算。更詳細的解釋,可以參考這個網頁https://blog.csdn.net/justidle/article/details/104414459。
高精度減法計算原理
在讀小學時,我們做減法都採用豎式方法,如圖 1 所示。 這樣,我們可以寫出兩個整數相減的算法。
我們就可以用 C++ 語言來模擬這個豎式減法的過程。我們可以考慮利用 C++ 的數組來存儲對應數據,假設用數組 A 存儲被減數 856 的每一位,具體來說就是 A1 存儲個位 6,A2 存儲十位 5,A3存儲百位 8;類似數組 A 的結構,使用數組 B 存儲減數 257;類似數組 A 的結構,使用數組 C 來存儲對應的差 599。兩數相加的結果就如圖 2 所示。這樣理論上來說,我們就可以計算無限大的數據。如上圖 2 所示,下表表示對應的存儲方式。
數組 A | 數組 B | 數組 C | |
[0] | 6 | 7 | 9 |
[1] | 5 | 5 | 9 |
[2] | 8 | 2 | 5 |
總結:利用數組存儲,突破存儲的限制。每個位置存儲 0 ~ 9 之間的數據。
高精度減法實現
思路
1、定義存儲數組。
2、被減數和減數確認。由於減法可能出現負數。
3、讀入數據到數組中。注意:保證被減數大於減數;倒序存放,也就是個位放在數組下標爲 0 的地方。
4、從個位開始模擬豎式加法的過程,完成整個減法。
5、刪除前導 0 。所謂前導零,就是出現類似這樣數據 01234,這個 0 實際是不需要的。
6、輸出減法的結果。倒序輸出減法的結果數組 C,因爲我們的個位是存儲在下標爲 0 的地方。
技術細節說明
定義存儲數組
根據題目的要求定義數組。這個部分代碼如下:
const int MAXN = 1e5+4; //根據題目的最大值。+4爲了防止A+B出現進位
char s1[MAXN] = {};//存儲字符串
char s2[MAXN] = {};//存儲字符串
char tmp[MAXN] = {};//交換用字符串
int a[MAXN] = {};//存儲加數A
int b[MAXN] = {};//存儲加數B
int c[MAXN] = {};//存儲和B
被減數和減數確認
由於減法可能出現負數,如 3-5=-2,我們在計算的時候,實際是使用 5-3=2,最後在結果前面添加負號。如果出現被減數小於減數的情況,要將兩者顛倒。
scanf("%s %s", s1, s2);//讀入字符串
int lena = strlen(s1);
int lenb = strlen(s2);
//判斷最終的結果符號
if ((lena<lenb) || (lena==lenb && strcmp(s1,s2)<0)) {
//被減數小於減數,結果爲負數
printf("-");
//交換數據
strcpy(tmp, s1);
strcpy(s1, s2);
strcpy(s2, tmp);
//更新長度數據
lena = strlen(s1);
lenb = strlen(s2);
}
讀入數據到數組
利用讀入字符串的方法讀入數據,再倒序寫入到對應的數組中。這個部分代碼如下:
//將字符串寫入到數組A中
for (int i=0; i<lena; i++) {
//倒序寫入
a[i] = s1[lena-i-1] - '0';
}
//將字符串寫入到數組B中
for (int i=0; i<lenb; i++) {
//倒序寫入
b[i] = s2[lenb-i-1] - '0';
}
模擬豎式減法
有兩個技術細節:如何判斷髮生借位。這個部分代碼如下:
//模擬豎式減法
for (int i=0; i<lena; i++) {
if (a[i]<b[i]) {
//有借位
a[i+1]--;
a[i] += 10;
}
c[i] = a[i] - b[i];
}
刪除前導零
因爲減法運算可能會出現最高位爲零,所以我們需要判斷是否需要刪除前導零。這個部分代碼如下:
//刪除前導零
for (int i=lena-1; i>=0; i--) {
//因爲我們是從索引 0 開始,所以最高位是保存在 len-1
if (0==c[i] && lena>1) {
//注意要有 lena>1 這個條件。考慮特殊情況,加法結果爲 00,我們實際要輸出 0。
lena--;
} else {
//第一個不是零的最高位,結束刪除
break;
}
}
輸出計算結果
採用倒序的方式輸出,因爲我們數據保存是倒序結構,也就是低位在前。
//逆序打印輸出
for (int i=lena-1; i>=0; i--) {
printf("%d", c[i]);
}
printf("\n");
例題和 AC 代碼
題目
題目鏈接
一本通 OJ:http://ybt.ssoier.cn:8088/problem_show.php?pid=1169。
我自己 OJ:http://47.110.135.197/problem.php?id=1216。
題目描述
求兩個大的正整數相減的差。
輸入
共 2 行,第 1 行是被減數 a,第 2 行是減數 b,不保證 a > b。每個大整數不超過 10005 位。
輸出
一行,即所求的差。
樣例輸入
9999999999999999999999999999999999999
9999999999999
樣例輸出
9999999999999999999999990000000000000
分析
題目告訴我們不超過 200 位,也就是 MAXN = 10005+4。
AC 代碼
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e4+4; //根據題目的最大值。+4爲了防止A+B出現進位
char s1[MAXN] = {};//存儲字符串
char s2[MAXN] = {};//存儲字符串
char tmp[MAXN] = {};//交換用字符串
int a[MAXN] = {};//存儲加數A
int b[MAXN] = {};//存儲加數B
int c[MAXN] = {};//存儲和B
int main() {
scanf("%s %s", s1, s2);//讀入字符串
int lena = strlen(s1);
int lenb = strlen(s2);
//判斷最終的結果符號
if ((lena<lenb) || (lena==lenb && strcmp(s1,s2)<0)) {
//被減數小於減數,結果爲負數
printf("-");
//交換數據
strcpy(tmp, s1);
strcpy(s1, s2);
strcpy(s2, tmp);
//更新長度數據
lena = strlen(s1);
lenb = strlen(s2);
}
//將字符串寫入到數組A中
for (int i=0; i<lena; i++) {
//倒序寫入
a[i] = s1[lena-i-1] - '0';
}
//將字符串寫入到數組B中
for (int i=0; i<lenb; i++) {
//倒序寫入
b[i] = s2[lenb-i-1] - '0';
}
//模擬豎式減法
for (int i=0; i<lena; i++) {
if (a[i]<b[i]) {
//有借位
a[i+1]--;
a[i] += 10;
}
c[i] = a[i] - b[i];
}
//刪除前導零
for (int i=lena-1; i>=0; i--) {
//因爲我們是從索引 0 開始,所以最高位是保存在 len-1
if (0==c[i] && lena>1) {
//注意要有 lena>1 這個條件。考慮特殊情況,加法結果爲 00,我們實際要輸出 0。
lena--;
} else {
//第一個不是零的最高位,結束刪除
break;
}
}
//逆序打印輸出
for (int i=lena-1; i>=0; i--) {
printf("%d", c[i]);
}
printf("\n");
return 0;
}