高精度運算總結
阿新 • • 發佈:2022-05-20
日期:2022年5月18日
注:本部落格中的程式碼僅供參考。
概念
資料型別 | 定義識別符號 | 資料範圍 |
整型 | int | -231~231-1(≈2×109) |
長整型 | long long | -263~263-1 |
無符號長整型 | unsigned long long | 0~264-1 |
上面給出的範圍的數稱為單精度數,對應的運算稱為單精度運算。
超出上面給出的範圍的數稱為高精度數(注:嘗試使用這些範圍儲存的話會導致溢位),對應的運算稱為高精度運算。
實現方法
將輸入的數字匯入某一陣列(注:不同的數需要匯入不同陣列),並在該陣列中模擬計算過程,最後將陣列中的數字輸出。
注:陣列中的每一個位置應只儲存一個位數的數字,否則在輸出時可能會出現問題。
程式碼(正片【確信】)
高精度加法
#include<bits/stdc++.h> using namespace std; char s[510];//這個字元陣列作為資料的“中轉站”,最終將資料匯入陣列中 struct bigInt{ int length;//資料的長度(位數) int v[510];//儲存資料的陣列 }; bigInt operator +(bigInt a,bigInt b){//重新載入+ int k=max(a.length,b.length);//比較尾數大小,將更大的位數作為之後相加的長度 for(int i=1;i<=k;++i) { a.v[i]+=b.v[i];//將每一位的數相加 } for(int i=1;i<=k;++i)//進位 { a.v[i+1]+=a.v[i]/10; a.v[i]%=10; } if(a.v[k+1]) { ++k; }//如果最高位也存在進位,增加k的長度 //嚴格來說,應該檢查不止一位。但因為加法最多會增加一位,所以檢查與否都無足輕重,然而在高精度乘法中會顯得尤為必要 a.length=k; return a; } bigInt A,B; int main(){ scanf("%s",s+1);//輸入資料。s+1代表資料從s[1]向後儲存 int n=strlen(s+1);//求資料的位數 for(int i=1;i<=n;++i) { A.v[i]=s[n-i+1]-'0';//賦值。-'0'可以使資料從字元轉化為數字。倒過來儲存有利於後面的計算(尤其是出現最高位進位時) } A.length=n;//賦值 scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;++i) { B.v[i]=s[n+1-i]-'0'; } B.length=n; A=A+B;//相加。這裡的+使用的是重新載入的+ for(int i=A.length;i>=1;--i) { printf("%d",A.v[i]);//輸出求得的和。由於資料是倒過來儲存的,輸出也應倒過來輸出 } return 0; }
高精度減法
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=10186; 4 struct bigInt{ 5 int length; 6 int v[N]; 7 }; 8 bigInt A,B,C; 9 char s[N]; 10 bigInt read(){//用於讀入資料的函式 11 memset(C.v,0,sizeof(C.v)); 12 scanf("%s",s+1); 13 int n=strlen(s+1); 14 for(int i=1;i<=n;++i) 15 { 16 C.v[i]=s[n+1-i]-'0'; 17 } 18 C.length=n; 19 return C; 20 } 21 bool operator >=(bigInt a,bigInt b){//比較兩數大小 22 int m=a.length; 23 int n=b.length; 24 if(m>n)//當m與n位數不同時,位數更大的數更大 25 { 26 return true; 27 } 28 if(m<n) 29 { 30 return false; 31 } 32 for(int i=n;i>0;--i) 33 { 34 if(a.v[i]>b.v[i])//當位數相同時,從最高位向下遍歷。如果某個數的某一位數字更大,則這個數更大 35 { 36 return true; 37 } 38 if(a.v[i]<b.v[i]) 39 { 40 return false; 41 } 42 } 43 return true; 44 } 45 bigInt operator -(bigInt a,bigInt b){ 46 int k=a.length; 47 for(int i=1;i<=k;++i) 48 { 49 if(a.v[i]<b.v[i]) 50 { 51 --a.v[i+1]; 52 a.v[i]+=10; 53 } 54 a.v[i]-=b.v[i]; 55 } 56 while(a.v[k]==0&&k>1) 57 { 58 --k; 59 } 60 a.length=k; 61 return a; 62 } 63 int main(){ 64 A=read(); 65 B=read(); 66 if(A>=B) 67 { 68 A=A-B; 69 }else{ 70 printf("-"); 71 A=B-A; 72 }//都是大的數減小的數,如果A<B時,在輸出資料前面輸出- 73 for(int i=A.length;i>0;--i) 74 { 75 printf("%d",A.v[i]); 76 }//同理,倒過來輸出 77 return 0; 78 }
高精度乘法
1 #include<bits/stdc++.h> 2 using namespace std; 3 char s[2500]; 4 int result[4005]={0},rlength; 5 struct bigInt{ 6 int length; 7 int v[2500]; 8 }; 9 bigInt operator *(bigInt a,bigInt b){ 10 for(int i=1;i<=a.length;++i) 11 { 12 for(int j=1;j<=b.length;++j) 13 { 14 result[i+j-1]+=a.v[i]*b.v[j]; 15 } 16 } 17 for(int i=1;i<=4001;++i) 18 { 19 result[i+1]+=result[i]/10; 20 result[i]%=10; 21 } 22 rlength=a.length+b.length; 23 while(result[rlength]==0&&rlength>1) 24 { 25 --rlength; 26 }//乘法最高位可能不止進一位,但因為所得結果的最多位數為A的位數,B的位數,可以從最多位數開始向後遍歷,在遇到不為0的數前且位數不為1時(考慮到結果為0的情況),將估計位數縮減 27 } 28 bigInt A,B; 29 int main(){ 30 scanf("%s",s+1); 31 int n=strlen(s+1); 32 for(int i=1;i<=n;++i) 33 { 34 A.v[i]=s[n+1-i]-'0'; 35 } 36 A.length=n; 37 scanf("%s",s+1); 38 n=strlen(s+1); 39 for(int i=1;i<=n;++i) 40 { 41 B.v[i]=s[n+1-i]-'0'; 42 } 43 B.length=n; 44 A=A*B; 45 for(int i=rlength;i>=1;--i) 46 { 47 printf("%d",result[i]); 48 } 49 return 0; 50 }
高精度除法
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5100; 4 struct bigInt{ 5 int length; 6 int v[N]; 7 }; 8 bigInt operator /(bigInt a,long long b){ 9 int k=a.length; 10 long long res=0,c=0; 11 for(int i=k;i>=1;--i)//因為除法在計算時與+、-、×相反,且資料儲存時依然是倒過來儲存的,所以此處要從最高位開始運算 12 { 13 res=res*10+a.v[i]; 14 a.v[i]=res/b; 15 res%=b; 16 /*這裡的原理是: 17 ①餘數(res)乘10並加上下一位 18 ②將餘數整除10的結果賦給要參加運算的那一位 19 ③餘數取餘10 20 */ 21 } 22 while(a.v[k]==0&&k>1) 23 { 24 --k;//統計位數 25 } 26 a.length=k; 27 return a; 28 } 29 bigInt A; 30 long long B; 31 char s[N]; 32 int main(){ 33 scanf("%s",s+1); 34 int n=strlen(s+1); 35 for(int i=1;i<=n;++i) 36 { 37 A.v[i]=s[n+1-i]-'0'; 38 } 39 A.length=n; 40 scanf("%lld",&B); 41 A=A/B; 42 for(int i=A.length;i>=1;--i) 43 { 44 printf("%d",A.v[i]); 45 } 46 return 0; 47 }
一些心得
- 使用高精度運算前要思考這道題是否需要高精度(如運算結果不會溢位就不需要用高精)/是否可以簡化(如題目要求或可以推出是高精和單精——寫了大半天高精和高精,結果最後發現是高精和單精的心情可不好)。如題目P1591(階乘數碼)中的階乘就可以簡化為高精乘單精。
- 在運算時也要注意開的一些數是否能完全囊括題目限定的資料範圍。例如這段程式碼(星號處為要點):
1 bigInt operator /(bigInt a,long long b){ 2 int k=a.length; 3 *int res=0,c=0; 4 for(int i=k;i>=1;--i) 5 { 6 res=res*10+a.v[i]; 7 a.v[i]=res/b; 8 res%=b; 9 } 10 while(a.v[k]==0&&k>1) 11 { 12 --k; 13 } 14 a.length=k; 15 return a; 16 }
"int res=0,c=0;"會導致餘數在超過範圍時出現運算錯誤,如計算9000000000/1000000000的結果是0。正確的處理如下:
1 bigInt operator /(bigInt a,long long b){ 2 int k=a.length; 3 *long long res=0,c=0; 4 for(int i=k;i>=1;--i) 5 { 6 res=res*10+a.v[i]; 7 a.v[i]=res/b; 8 res%=b; 9 } 10 while(a.v[k]==0&&k>1) 11 { 12 --k; 13 } 14 a.length=k; 15 return a; 16 }