1. 程式人生 > 實用技巧 >高精度加法

高精度加法

吶!眾所周知,資料是有範圍的

\(Python \&\& Java:\) sorry,高精度真的可以為所欲為

本文為部落格園 ShyButHandsome的原創作品,轉載請註明出處

注:右邊有目錄,方便快速瀏覽

需求原因

C++中的資料型別是有範圍的

實現思路

雖然內建資料類型範圍不大

但是字串能很長啊!

我們將加數的每一位都看作是一個字元的話

那我們就得到了兩個字串

程式碼實現

解釋都在程式碼裡了

#include <iostream>

// EXIT_SUCCESS在stdlib庫中
#include <cstdlib>

// strlen函式需要cstring庫
#include <cstring>

using namespace std;

// 500+6防止陣列越界
const int LENGTH_LIMIT = 5e2 + 6;

// char表示的整數 減去一個‘0’就可以得到響應整數的ascii
const int INT_CHANGER = '0';

// 個位數不能超過10
const int SINGLE_LIMIT = 10;

char stringA[LENGTH_LIMIT];
char stringB[LENGTH_LIMIT];

int invertedA[LENGTH_LIMIT];
int invertedB[LENGTH_LIMIT];
int sum[LENGTH_LIMIT];

int main(void)
{

    cin >> stringA >> stringB;

    // 倒序儲存
    int lengthA = strlen(stringA);
    for (int i = 0; i < lengthA; i++)
    {
        // 由於strlen返回的長度是符合正常思維(從1開始)
        // 但我們是從0開始,所以要額外 - 1
        invertedA[i] = stringA[lengthA - i - 1] - INT_CHANGER;
    }

    int lenghtB = strlen(stringB);
    for (int i = 0; i < lenghtB; i++)
    {
        invertedB[i] = stringB[lenghtB - i - 1] - INT_CHANGER;
    }

    // 和的最大長度是a,b最大值+1
    const int MAX_LENGTH = ((lengthA < lenghtB) ? lenghtB : lengthA) + 1;

    // 進位
    int carry = 0;

    // 模擬計算
    for (int i = 0; i < MAX_LENGTH; i++)
    {
        // 對應位相加
        sum[i] = invertedA[i] + invertedB[i] + carry;
        // 記錄下進位
        carry = sum[i] / SINGLE_LIMIT;
        // 進位操作
        sum[i] %= SINGLE_LIMIT;
    }

    // 這裡length選擇放在迴圈外,因為兩個迴圈都會用到它
    // +1是因為strlen返回的陣列長度是從1開始計數的
    int length = MAX_LENGTH;

    // 刪除前導零(確定數字實際長度)
    // 這裡取length>0,因為
    // 如果刪到最後一位就必須得保留,不管它是不是0
    for (length; length > 0; length--)
    {
        if (sum[length] == 0)
        {
            // 接著處理下一位
            continue;
        }
        else
        {
            // 如果遇到一位非零就說明前導零刪乾淨了
            // 接著刪就會刪去數字中的0了
            break;
        }
    }

    // 正序輸出
    for (length; length >= 0; length--)
    {
        cout << sum[length];
    }

    // 額外輸出一個換行
    cout << endl;

    // EXIT_SUCCESS means 0
    return EXIT_SUCCESS;
}

再給出一份用STL寫的:

#include <bits/stdc++.h>

using namespace std;

int main(void)
{
    string strA, strB;
    cin >> strA >> strB;

    vector<int> numA, numB, sum;
    
    for (int i = strA.size() - 1; i >= 0; i--)
    {
        numA.push_back(strA[i] - '0');
    }
    for (int i = strB.size() - 1; i >= 0; i--)
    {
        numB.push_back(strB[i] - '0');
    }

    int carry = 0;
    for (int i = 0; i < (max(numA.size(), numB.size())); i++)
    {
        sum.push_back(carry);
        // If lenA != lenB
        sum[i] += (i > (numA.size() - 1) ? 0 : numA[i]) + (i > (numB.size() - 1) ? 0 : numB[i]);
        carry = (sum[i]) / 10;
        sum[i] %= 10;
    }

    if (carry != 0)
    {
        cout << carry;
    }

    for (int i = sum.size() - 1; i >= 0; i--)
    {
        cout << sum[i];
    }

    cout << endl;

    return 0;
}

易錯點

下面是我總結出的幾個易錯點

每個易錯點都給出了測試資料

可以利用右上角的複製按鈕快速複製

沒有處理進位

包括

  • 每次沒有計算上次的進位啊
  • 每次沒有加上上次的進位啊
  • 邊界沒有進位啊
  • 等等

測試資料:

輸入:

56546876443156448001
56453168410002134684

輸出:

113000044853158582685

刪除前導零錯誤

測試資料:

輸入 1:

11111111111111111111111111
9999999999999999999999999999999999

輸出 1:

10000000011111111111111111111111110

輸入 2:

0
0

輸出 2:

0

STL還要注意長度不一樣

輸入:

350204156568812004512541458715942878921069349854678
5120132301230048014584016540526804565814706519846494321001345794503242112976563432120

輸出:

5120132301230048014584016540526804916018863088658498833542804510446121034045913286798