1. 程式人生 > >【C++高精度演算法】

【C++高精度演算法】

前言:由於計算機運算是有模運算,資料範圍的表示有一定限制,如整型int(C++中int 與long相同)表達範圍是(-2^31~2^31-1),unsigned long(無符號整數)是(0~2^32-1),都約為幾十億.如果採用實數型,則能儲存最大的double只能提供15~16位的有效數字,即只能精確表達數百萬億的數.因此,在計算位數超過十幾位的數時,不能採用現有型別,只能自己程式設計計算.
高精度計算通用方法:高精度計算時一般用一個數組來儲存一個數,陣列的一個元素對應於數的一位(當然,在以後的學習中為了加快計算速度,也可用陣列的一個元素表示數的多位數字,暫時不講),表示時,由於數計算時可能要進位,因此為了方便,將數由低位到高位依次存在陣列下標對應由低到高位置上,另外,我們申請陣列大小時,一般考慮了最大的情況,在很多情況下,表示有富餘,即高位有很多0,可能造成無效的運算和判斷,因此,我們一般將陣列的第0個下標對應位置來儲存該數的位數.如數:3485(三千四百八十五),表達在陣列a[10]上情況是:


下標  0    1    2    3     4    5    6    7    8    9  
內容  4    5    8    4     3    0    0    0    0    0
說明:位數   個位  十位  百位 千位
具體在計算加減乘除時方法就是小學時採用的列豎式方法.
注:高精度計算時一般用正數,對於負數,通過處理符號位的修正.
一.高精度數的儲存
1.如對數採用的字串輸入

#include <iostream>
#include <cstring>
using namespace std;
const int N=100;//最多100位
int main()
{
    int a[N+1],i;
    string s1;
    cin>>s1;//數s1
    memset(a,0,sizeof(a)); //陣列清0
    a[0]=s1.length(); //位數
    for(i=1; i<=a[0]; i++) a[i]=s1[a[0]-i]-'0'; //將字元轉為數字並倒序儲存.
    return 0;
}

2.直接讀入
#include <iostream>
using namespace std;
const int N=100;//最多100位
int main()
{
    int a[N+1],i,s,key;
    cin>>key;//數key
    memset(a,0,sizeof(a)); //陣列清0
    i=0;//第0位
    while(key)  //當key大於0
    {
        a[++i]=key%10;//取第i位的數
        key=key/10;
    }
    a[0]=i; //共i位數
    return 0;
}

3.直接初始化(用a[]儲存)

初始化為0: memset(a,0,sizeof(a));
初始化為1: memset(a,0,sizeof(a));a[0]=1;a[1]=1;

以下程式都只寫函式,不寫完整程式,所有高精度數儲存都滿足上述約定。
二.高精度數比較
int compare(int a[],int b[])   //比較a和b的大小關係,若a>b則為1,a<b則為-1,a=b則為0
{
    int i;
    if (a[0]>b[0]) return 1;//a的位數大於b則a比b大
    if (a[0]<b[0]) return -1;//a的位數小於b則a比b小
    for(i=a[0]; i>0; i--) //從高位到低位比較
    {
        if (a[i]>b[i]) return 1;
        if (a[i]<b[i]) return -1;
    }
    return 0;//各位都相等則兩數相等。
}

三、高精度加法
void Plus(int a[],int b[]) //計算a=a+b
{
    int i,k;
    k=a[0]>b[0]?a[0]:b[0]; //k是a和b中位數最大的一個的位數
    for(i=1; i<=k; i++)
    {
        a[i+1]+=(a[i]+b[i])/10;  //若有進位,則先進位
        a[i]=(a[i]+b[i])%10;
    }  //計算當前位數字,注意:這條語句與上一條不能交換。
    if(a[k+1]>0) a[0]=k+1;  //修正新的a的位數(a+b最多隻能的一個進位)
    else a[0]=k;
}
四、高精度減法
int gminus(int a[],int b[]);//計算a=a-b,返加符號位0:正數 1:負數
{
    int flag,i
    flag=compare(a,b); //呼叫比較函式判斷大小
    if (falg==0)//相等
    {
        memset(a,0,sizeof(a));    //若a=b,則a=0,也可在return前加一句a[0]=1,表示是 1位數0
        return 0;
    }
    if(flag==1) //大於
    {
        for(i=1; i<=a[0]; i++)
        {
            if(a[i]<b[i])
            {
                a[i+1]--;    //若不夠減則向上借一位
                a[i]+=10;
            }
            a[i]=a[i]-b[i];
        }
        while(a[a[0]]==0) a[0]--; //修正a的位數
        return 0;
    }
    if (flag==-1)//小於  則用a=b-a,返回-1
    {
        for(i=1; i<=b[0]; i++)
        {
            if(b[i]<a[i])
            {
                b[i+1]--;    //若不夠減則向上借一位
                b[i]+=10;
            }
            a[i]=b[i]-a[i];
        }
        a[0]=b[0];
        while(a[a[0]]==0) a[0]--; //修正a的位數
        return -1;
    }
}
五、高精度乘法1(高精度乘單精度數,單精度數是指通常的整型數)
void Mul(int a[],long key) //a=a*key,key是單精度數
{
    int i,k;
    if (key==0)
    {
        memset(a,0,sizeof(a));    //單獨處理key=0
        a[0]=1;
        return 0;
    }
    for(i=1; i<=a[0]; i++)a[i]=a[i]*key; //先每位乘起來
    for(i=1; i<=a[0]; i++)
    {
        a[i+1]+=a[i]/10;    //進位
        a[i]%=10;
    }
//注意上一語句退出時i=a[0]+1
    while(a[i]>0)
    {
        a[i+1]=a[i]/10;    //繼續處理超過原a[0]位數的進位,修正a的位數
        a[i]=a[i]%10;
        i++;
        a[0]++;
    }
}