C++的高精度減法

爲什麼需要高精度計算

對於 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;
}
發佈了167 篇原創文章 · 獲贊 11 · 訪問量 104萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章