C++ 的高精度除法

爲什麼需要高精度計算

對於 C++ 而言,最大的數據爲 long long(64b,8位),對於超過 8B 的數據,C++ 沒有對應的數據類型進行表示。所以我們需要知道高精度計算。更詳細的解釋,可以參考這個網頁https://blog.csdn.net/justidle/article/details/104414459

高精度除法計算原理

在讀小學時,我們做除法都採用豎式方法計算。被除數從高位開始,和被除數對齊,諸位“試商”,“試商”後被除數減去“試商”的數的乘積,如下圖所示。

採用計算機做高精度除法時,模擬日常除法的步驟。但計算機不可能做“試商”,這時,我們可以採用減法來模擬。

試商過程

1、將除數移動和被除數對齊,位數不夠時,補 0。

2、利用被除數減去除數,一直減到被除數小於除數,減的次數,就是“試商”的結果,每移動一次。

3、重複上述步驟,一直到被除數和除數的位數相等爲止。

舉例說明

舉一個例子,比如 524134 除以 123,結果是 4261 ,餘數爲 31。

1、第一位 4 的來源是我們把 524 和 123 對齊,然後進行循環減法,循環了 4 次,餘 32;

2、將 32134 的前三位 321 繼續和 123 對齊,循環減法 2 次,餘 75;

3、把 7534 的前三位 753 和 123 對齊,循環減法 6 次,餘 15;

4、將 154 和 123 對齊,只能減 1 次,餘 31。

所以 524134 除以 123,結果是 4261,餘數爲 31。

計算過程

  A B 餘數
1 524134 123 4 32
2 032134 0123 2 75
3 007534 00123 6 15
4 000154 000123 1 31
5 000031 0000123   31

高精度除法實現

思路

1、定義存儲數組。

2、讀入數據處理。

3、試商過程。

4、刪除前導 0 。所謂前導零,就是出現類似這樣數據 01234,這個 0 實際是不需要的。

5、輸出結果。倒序輸出減法的結果數組 C,因爲我們的個位是存儲在下標爲 0 的地方。

技術細節說明

定義存儲數組

根據題目的要求定義數組。這個部分代碼如下:

const int MAXN = 1e5+4; //根據題目的最大值。+4爲了防止A+B出現進位
char s1[MAXN] = {};//存儲字符串
char s2[MAXN] = {};//存儲字符串
int a[MAXN] = {};//存儲被除數
int b[MAXN] = {};//存儲除數B
int c[MAXN] = {};//存儲商
int tmp[MAXN] = {};

注意:這裏的數組索引 0 所在的數據表示本數據的長度。

輔助函數

比較大小函數

用於比較兩個 int 類型數組內數據大小。返回值和 C++ 庫函數 campare() 相同。

int compare(int a[], int b[]) {
    //索引爲0的數據爲數組長度
    if (a[0]>b[0]) {
        return 1;
    } else if (a[0]<b[0]) {
        return -1;
    }

    //逐位比較
    for (int i=a[0]; i>0; i--) {
        if (a[i]>b[i]) {
            return 1;
        } else if (a[i]<b[i]) {
            return -1;
        }
    }

    return 0;
}

移位操作函數

void numcpy(int a[],int b[],int dest) {
    //將數組右移,使兩個數組右端對齊,形參q數組儲存右移後的結果
    for (int i=1;i<=a[0];i++) {
        b[i+dest-1] =a[i];
    }
    b[0] = a[0]+dest-1;
}

讀入數據並處理

這裏我們先考慮以下幾種情況,即 15/4=3 ... 3、-8/3=-2 ... -2、10/(-3)=-3 ... 1 或者 -3*(-2)=1 ... -1,也就是說,需要對輸入數據的正負號進行判斷。這個部分代碼如下:

    scanf("%s %s", s1, s2);//讀入字符串

    //處理負數
    bool flaga = false;//乘數a的符號
    if ('-'==s1[0]) {
        flaga = true;
        strcpy(s1, &s1[1]);//刪除負號
    }
    bool flagb = false;//乘數b的符號
    if ('-'==s2[0]) {
        flagb = true;
        strcpy(s2, &s2[1]);//刪除負號
    }

    //處理輸出的負號
    if (true==flaga && false==flagb) {
        //商爲負數
        printf("-");
    }

    //處理乘數1
    int len = strlen(s1);
    a[0] = len;
    for (int i=0; i<len; i++) {
        a[len-i]=s1[i]-'0';
    }

    //處理乘數2
    len = strlen(s2);
    b[0] = len;
    for (int i=0; i<len; i++) {
        b[len-i]=s2[i]-'0';
    }

試商

這個部分代碼如下:

    if (0==compare(a,b)) {
        //兩數相等
        printf("1\n0\n");
        return 0;
    } else if (-1==compare(a,b)) {
        //被除數小,除數大
        printf("0\n");//輸出除數
        if (true==flaga) {
            printf("-");
        }
        printf("%s\n", s1);
        return 0;
    } else {
        c[0] = a[0]-b[0]+1;
        for (int i=c[0]; i>0; i--) {
            memset(tmp, 0, sizeof(tmp));
            //高位對齊
            numcpy(b,tmp,i);

            //
            while (compare(a, tmp)>=0) {
                c[i]++;
                //減法
                for (int j=1; j<=a[0]; j++) {
                    if (a[j]<tmp[j]) {
                        a[j+1]--;
                        a[j]+=10;
                    }
                    a[j]-=tmp[j];
                }

                int k=a[0];
                while (a[k]==0) {
                    k--;
                }
                a[0]=k;
            }
        }

        //控制最高位的0
        while (c[0]>0 && c[c[0]]==0) {
            c[0]--;
        }
    }

輸出計算結果

採用倒序的方式輸出,因爲我們數據保存是倒序結構,也就

    //逆序打印輸出商和餘數
    for (int i=c[0]; i>0; i--) {
        printf("%d", c[i]);
    }
    printf("\n");
    if (0==a[0]) {
        printf("0\n");
    } else {
        if (true==flaga) {
            printf("-");
        }
        for (int i=a[0]; i>0; i--) {
            printf("%d", a[i]);
        }
        printf("\n");
    }

輸出格式需要根據實際題目要求進行修改。

例題和 AC 代碼

題目

題目鏈接

一本通 OJ:http://ybt.ssoier.cn:8088/problem_show.php?pid=1308

我的OJ:http://47.110.135.197/problem.php?id=1213

題目描述

高精除以高精,求它們的商和餘數。

輸入

輸入兩個低於300位的正整數。

輸出

輸出商和餘數。

樣例輸入

1231312318457577687897987642324567864324567876543245671425346756786867867867
1231312318767141738178325678412414124141425346756786867867867

樣例輸出

999999999748590
179780909068307566598992807564736854549985603543237528310337

分析

題目告訴我們不超過 300 位,也就是 MAXN = 300+4。

A 代碼

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 300+4; //根據題目的最大值。+4爲了防止A+B出現進位
char s1[MAXN] = {};//存儲字符串
char s2[MAXN] = {};//存儲字符串
int tmp[MAXN] = {};//交換用字符串
int a[MAXN] = {};//存儲加數A
int b[MAXN] = {};//存儲加數B
int c[MAXN] = {};//存儲和B

int compare(int a[], int b[]) {
    //索引爲0的數據爲數組長度
    if (a[0]>b[0]) {
        return 1;
    } else if (a[0]<b[0]) {
        return -1;
    }

    //逐位比較
    for (int i=a[0]; i>0; i--) {
        if (a[i]>b[i]) {
            return 1;
        } else if (a[i]<b[i]) {
            return -1;
        }
    }

    return 0;
}

void numcpy(int a[],int b[],int dest) {
    //將數組右移,使兩個數組右端對齊,形參q數組儲存右移後的結果
    for (int i=1;i<=a[0];i++) {
        b[i+dest-1] =a[i];
    }
    b[0] = a[0]+dest-1;
}

int main() {
    scanf("%s %s", s1, s2);//讀入字符串

    //處理負數
    bool flaga = false;//乘數a的符號
    if ('-'==s1[0]) {
        flaga = true;
        strcpy(s1, &s1[1]);//刪除負號
    }
    bool flagb = false;//乘數b的符號
    if ('-'==s2[0]) {
        flagb = true;
        strcpy(s2, &s2[1]);//刪除負號
    }

    //處理輸出的負號
    if (true==flaga && false==flagb) {
        //商爲負數
        printf("-");
    }

    //處理乘數1
    int len = strlen(s1);
    a[0] = len;
    for (int i=0; i<len; i++) {
        a[len-i]=s1[i]-'0';
    }

    //處理乘數2
    len = strlen(s2);
    b[0] = len;
    for (int i=0; i<len; i++) {
        b[len-i]=s2[i]-'0';
    }

    if (0==compare(a,b)) {
        //兩數相等
        printf("1\n0\n");
        return 0;
    } else if (-1==compare(a,b)) {
        //被除數小,除數大
        printf("0\n");//輸出除數
        if (true==flaga) {
            printf("-");
        }
        printf("%s\n", s1);
        return 0;
    } else {
        c[0] = a[0]-b[0]+1;
        for (int i=c[0]; i>0; i--) {
            memset(tmp, 0, sizeof(tmp));
            //高位對齊
            numcpy(b,tmp,i);

            //
            while (compare(a, tmp)>=0) {
                c[i]++;
                //減法
                for (int j=1; j<=a[0]; j++) {
                    if (a[j]<tmp[j]) {
                        a[j+1]--;
                        a[j]+=10;
                    }
                    a[j]-=tmp[j];
                }

                int k=a[0];
                while (a[k]==0) {
                    k--;
                }
                a[0]=k;
            }
        }

        //控制最高位的0
        while (c[0]>0 && c[c[0]]==0) {
            c[0]--;
        }
    }

    //逆序打印輸出商和餘數
    for (int i=c[0]; i>0; i--) {
        printf("%d", c[i]);
    }
    printf("\n");
    if (0==a[0]) {
        printf("0\n");
    } else {
        if (true==flaga) {
            printf("-");
        }
        for (int i=a[0]; i>0; i--) {
            printf("%d", a[i]);
        }
        printf("\n");
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章