1. 程式人生 > 其它 >高精度運算總結

高精度運算總結

日期: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 }