爲什麼需要高精度計算
對於 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;
}