1. 程式人生 > 實用技巧 >整數的高精度加、減法的實現

整數的高精度加、減法的實現

整數的高精度加、減法的實現

由於C++不自帶高精,所以常常無法處理一些大數的運算。最近我學習了高精度,將所學內容整理於此。其實是為了應付這週五的學習筆記(我剛接觸C++,也是第一次用部落格,,如果有什麼錯誤請多多包涵,,)


  • 為什麼要用高精度?
    因為long long最大也只能存20位的數,強行輸入大數會爆 掉

  • 那要怎麼實現高精度計算?
    那就用用字元陣列或string類儲存大數,然後根據小學生加減法原理進行加減乘除,得出結果。


    接下來先對最簡單的高精度加法進行實現。

整數的高精度加法 (負數見減法部分)

 先從離我們最近的入手:小學生加法原理
  我們計算$a_1b_1c_1d_1+b_2c_2d_2$時,先是個位$d_1+d_2$,滿$10$進$1$,可以理解為滿$10$,就讓$d_1+d_2$的結果減去$10$,記為個位,下一次計算$c_1+c_2$時就同時加上一個$1$,如果滿$10$同上操作,記為十位。如果都不滿$10$,那就不減$10$也不進$1$。其他依次往前推。
  這樣我們就不難得出整個演算法的核心:

c for (i=0;i<=c3;++i)
    {
        c[i]+=a1[i]+a2[i];   //a1,a2用來儲存兩個相加的數
        c[i+1]=c[i]/10;   //進位的實現
        c[i]=c[i]%10;
    }

那具體要怎麼實現?
輸入部分不必多說,輸入後我們必須先知道兩個字元陣列的最長的長度,由此來知道進行加法的最高位數,這也是字元陣列的便利所在:

c1=strlen(s1);
c2=strlen(s2);
len=max(c1,c2);

之後將字元陣列逆序賦值給整型陣列,為什麼呢?()

for (i=0;i<c1;++i)
        a1[i]=s1[c1-1-i]-'0';//要減去個‘0’

for(i=0;i<c2;++i)
        a2[i]=s2[c2-1-i]-'0';

之後就可以進行加法啦!

for (i=0;i<=len;++i)//這裡len為什麼要用等號?
{
     c[i]+=a1[i]+a2[i];
     c[i+1]=c[i]/10;
     c[i]=c[i]%10;
}

然後逆序輸出,記得刪除前導0

if (c[len]==0) --len;//刪除前導0

for (i=len;i>=0;--i)
    cout<<c[i];

這樣就大工造成啦!
附上我自己第一次做時候的程式碼:

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    char s1[505]={0},s2[505]={0};
    int a1[505]={0},a2[505]={0},c[506]={0};
    int i=0,c1=0,c2=0,c3=0;

    cin>>s1>>s2;

    c1=strlen(s1);
    c2=strlen(s2);
    c3=max(c1,c2);

    for (i=0;i<c1;++i)
        a1[i]=s1[c1-1-i]-'0';

    for(i=0;i<c2;++i)
        a2[i]=s2[c2-1-i]-'0';

    for (i=0;i<=c3;++i)
    {
        c[i]+=a1[i]+a2[i];
        c[i+1]=c[i]/10;
        c[i]=c[i]%10;
    }

    if (c[c3]==0)
    {
        for (i=c3-1;i>=0;--i)
            cout<<c[i];
    }
    else
    {
        for (i=c3;i>=0;--i)
            cout<<c[i];

    }
  return 0;
}


接下來是小學生減法(結果負數也得輸出,以下用string做答)

也先從原理入手:
嘛,,豎式減法,如果$\ a_1b_1c_1d_1-b_2c_2d_2$,先個位相減,也就是$\ d1-d2$,若小於$\ 0$,則讓$\ c_1$減去$\ 1$,再把$\ d1+d2$加上10,記為個位,然後進行$\ c_1-c_2$,如果結果大於$\ 0$,就直接記為十位,再進行$\ b_1-b_2$,依次往前推。
這樣可以得出:

for (i=0;i<lim;++i)//lim為兩個數中最高位的位數
    {
        ans[i]+=arr1[i]-arr2[i];
        if (ans[i]<0)
        {
            --ans[i+1];
            ans[i]+=10;
        }
    }

我用 --ans[i+1] 來實現退位。

然而這只是應付arr1中存的數比arr2中大的情況,那麼:
當arr1<arr2時,用swap進行交換,並用 bool flag 記錄結果正負

if (( n1=str1.size())==(n2=str2.size())&&str1<str2||n1<n2)
    {
        str1.swap(str2);//保證str1>=str2
        flag=1;
    }

這樣就可以隨意進行任意整數的加減了,其餘部分基本與加法一致。

搭嘎!刪除前導0這裡有坑:

  • 和加法不一樣,減法的最高位數可能是多個0,可以用迴圈進行刪除:
while (ans[lim-1]==0) lim--;
  • 如果結果等於 0 ,則要特殊考慮:
if (lim-1<0) cout<<0;

貼程式碼:

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

int main()
{
    string str1,str2;
    int n1=0,n2=0,lim=0,i=0;//n用來存字串長度
    bool flag=0;
    int arr1[10500]={0},arr2[10500]={0},ans[10500]={0};

    cin>>str1>>str2;

    if (( n1=str1.size())==(n2=str2.size())&&str1<str2||n1<n2)
    {
        str1.swap(str2);//保證str1>=str2
        flag=1;
    }

    n1=str1.size();
    n2=str2.size();

    for (i=n1-1;i>=0;--i) arr1[n1-1-i]=str1[i]-'0';
    for (i=n2-1;i>=0;--i) arr2[n2-1-i]=str2[i]-'0';

    lim=max(n1,n2);

    for (i=0;i<lim;++i)
    {
        ans[i]+=arr1[i]-arr2[i];
        if (ans[i]<0)
        {
            --ans[i+1];
            ans[i]+=10;
        }
    }

    while (ans[lim-1]==0) lim--;

    if (flag==true) cout<<'-';

    if (lim-1<0) cout<<0;

    for (i=lim-1;i>=0;--i)
        cout<<ans[i];



return 0;
}

順帶一提,我之前用字元陣列實現高精減法的是這個的2倍長


寫完之後感覺格式好亂,,,


高精度減法的實現我參考了這個
別人說的蠻詳細的,,不像我,,很多地方都懶得解釋