高精度

· · 个人记录

高精度计算

————高精度加法是信息学的一种重要算法。这种算法使用多个存储单位进行计算,因此它的计算范围超过一般使用一个存储单位的算法。也是一些信息学竞赛的常考题目。

一、高精度计算要注意的几个问题

1.数据的接收方法和存储方法

高精度计算一般输入的数都较长,需要用字符串的方式读入,利用字符串函数和操作运算,将每一位数取出,转存到数组中。

2.进位,借位处理方法

  c[i]=a[i]+b[i];
  if(c[i]>=10) 
  {
      c[i]%=10; c[i]++;  
  }
  if(a[i]<b[i])
  {
      a[i+1]--;a[i]+=10;
  }  
  c[i]=a[i]-b[i];
  c[i+j-1]=a[i]*b[j]+x+c[i+j-1];
  x=c[i+j-1];
  c[i+j-1]%=10;

二、高精度程序

1.高精度加法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char a1[1000],b1[1000];
int a[1000],b[1000],c[1000];
int main()
{
    scanf("%s",a1);//读入两个字符串,为两个加数 。 
    scanf("%s",b1);//读入两个字符串,为两个加数 。 
    if(a1[0]==48&&b1[0]==48)//特判两个数均为零,以免前导0被删掉。 
    {
        cout<<"0"<<endl;//如果两个数都为0,直接输出0。 
        return 0;
    }
    int lena=strlen(a1),lenb=strlen(b1);//lena为a的长度,lenb为b的长度。 
    for(int i=0; i<lena; i++)
    {
        a[lena-i-1]=int(a1[i]-48);//将a倒序并强制转换为数字,用整形数组存储,以便计算。 
    }
    for(int i=0; i<lenb; i++)
    {
        b[lenb-i-1]=int(b1[i]-48);//将b倒序并强制转换为数字,用整形数组存储,以便计算。 
    }
    int m=max(lena,lenb);//求a与b的最大长度。 
    for(int i=0; i<m; i++)
    {
        c[i]+=a[i]+b[i];//将a[i]+b[i]的值与前面的进位相加,赋给c[i]。 
        while(c[i]>=10)//加完当c[i]>10时要进位。 
        {
            c[i+1]++;//加法进位 。 
            c[i]-=10;//加法进位 。 
        }
    }
    m++;
    while(c[m]==0)
        m--; //删除前导0。 
    for(int i=m; i>=0; i--)
        cout<<c[i];//输出。 
    return 0;
}

2.高精度减法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[100000],b[100000],c[100000],lena,lenb,lenc,i;
int main()
{
    char n[100000],n1[100000],n2[100000];
    cin>>n1;//读入字符串n1,为被减数。 
    cin>>n2;//读入字符串n2,为减数。 
    if(strlen(n1)<strlen(n2)||((strlen(n1))==strlen(n2))&&(strcmp(n1,n2)<0))
    {
        strcpy(n,n1);
        strcpy(n1,n2);
        strcpy(n2,n);
        cout<<"-";
    }
    /*比较减数与被减数大小关系。当减数位数小于被减数,或位数相等但减数小于被减数时输出, 
      负号("-")并把两个数交换,以便计算 。 
    */ 
    lena=strlen(n1);//lena为a的长度。 
    lenb=strlen(n2);//lenb为b的长度。 
    for(i=0; i<=lena-1; i++)
    {
        a[lena-i]=int (n1[i]-48);//将a倒序并强制转换为数字,用整形数组存储,以便计算。
    }
    for(i=0; i<=lenb-1; i++)
    {
        b[lenb-i]=int (n2[i]-48);//将b倒序并强制转换为数字,用整形数组存储,以便计算。
    }
    i=1;
    while(i<=lena||i<lenb)//i小于任何一个数的长度时进行减法。 
    {
        if(a[i]<b[i])
        { 
            a[i]+=10;//减法借位 。 
            a[i+1]--;//减法借位。 
        }
        c[i]=a[i]-b[i];
        i++;
    }
    lenc=i;
    while((c[lenc]==0)&&(lenc>1))
    {
        lenc--;//删除前导0。 
    }
    for(i=lenc; i>=1; i--)
    {
        cout<<c[i];//输出。 
    }
    return 0;
}

3.高精度乘法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[5000],b[5000],c[5000];
int la,lb,lc,i,j,n,ls,ii;
char s[5000];
int p;
int main()
{
    while (scanf("%s %d",s,&n)!=EOF)//读入 
    {
        ls=strlen(s);
        for (p=0;p<ls;p++) if (s[p]=='.') break;//找到小数点的位置 
        la=0;
        for (i=0;i<ls;i++)//丢掉小数点把所有的数倒序存在a数组中 
        {
            if (s[ls-i-1]=='.') continue;
            la++;
            a[la]=s[ls-i-1]-'0';
        }
        p=(ls-1-p)*n;//求出小数点的位数 
        memcpy(c,a,sizeof(c));//把c数组赋值成a数组,因为n=1时不会算
        //其实也有办法算的,可以把b数组搞成1,然后算一次,不过会慢点 
        lc=la;//c的长度 
        for (ii=2;ii<=n;ii++)//一次一次的乘 
        {
            memcpy(b,c,sizeof(b));//把b数组赋值成c数组 
            memset(c,0,sizeof(c));
            lb=lc;//位数 
            lc=la+lb;//位数加起来 
            for (i=1;i<=la;i++)//高精度乘法 
            {
                for (j=1;j<=lb;j++)
                {
                    c[i+j-1]+=a[i]*b[j];//先加进去 
                    c[i+j]+=c[i+j-1]/10;//进位 
                    c[i+j-1]%=10;//取模 
                }
            }
            while (c[lc]==0&&lc>p) lc--;
        }
        int a1,b1;//两个指针,丢掉首尾的0 
        a1=lc;
        b1=1;
        while (c[a1]==0&&a1>p) a1--;
        while (c[b1]==0) b1++;
        for (i=a1;i>=b1;i--)//正着输出 
        {
            if (i==p) printf(".");//嗯小数点有p位,so 
            printf("%d",c[i]);
        }
        printf("\n");//记得换行 
    } 
    return 0;
}

4.高精度小数幂

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[5000],b[5000],c[5000];
int la,lb,lc,i,j,n,ls,ii;
char s[5000];
int p;
int main()
{
    while (scanf("%s %d",s,&n)!=EOF)//读入 
    {
        ls=strlen(s);
        for (p=0;p<ls;p++) if (s[p]=='.') break;//找到小数点的位置 
        la=0;
        for (i=0;i<ls;i++)//丢掉小数点把所有的数倒序存在a数组中 
        {
            if (s[ls-i-1]=='.') continue;
            la++;
            a[la]=s[ls-i-1]-'0';
        }
        p=(ls-1-p)*n;//求出小数点的位数 
        memcpy(c,a,sizeof(c));//把c数组赋值成a数组,因为n=1时不会算
        //其实也有办法算的,可以把b数组搞成1,然后算一次,不过会慢点 
        lc=la;//c的长度 
        for (ii=2;ii<=n;ii++)//一次一次的乘 
        {
            memcpy(b,c,sizeof(b));//把b数组赋值成c数组 
            memset(c,0,sizeof(c));
            lb=lc;//位数 
            lc=la+lb;//位数加起来 
            for (i=1;i<=la;i++)//高精度乘法 
            {
                for (j=1;j<=lb;j++)
                {
                    c[i+j-1]+=a[i]*b[j];//先加进去 
                    c[i+j]+=c[i+j-1]/10;//进位 
                    c[i+j-1]%=10;//取模 
                }
            }
            while (c[lc]==0&&lc>p) lc--;
        }
        int a1,b1;//两个指针,丢掉首尾的0 
        a1=lc;
        b1=1;
        while (c[a1]==0&&a1>p) a1--;
        while (c[b1]==0) b1++;
        for (i=a1;i>=b1;i--)//正着输出 
        {
            if (i==p) printf(".");//小数点有p位。 
            printf("%d",c[i]);
        }
        printf("\n");//记得换行 
    } 
    return 0;
}

5.高精度开根号

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#define Ll unsigned long long
using namespace std;
const Ll NN=1e9;
const int N=9;
struct H
{
    Ll a[1500],len;
    H()
    {
        memset(a,0,sizeof a);
        len=1;
    }
};
int n;
H s;
void init(H &a) //读入
{
    string s;
    cin>>s;
    int l;
    a.len=0;
    for(int r=s.length()-1; r>=0; r-=N)
    {
        a.len++;
        if(r>=N-1)l=r-N+1;
        else l=0;
        for(int i=l; i<=r; i++)a.a[a.len]=a.a[a.len]*10+s[i]-48;
    }
}
void outit(H a) //输出
{
    printf("%d",a.a[a.len]);
    for(int i=a.len-1; i; i--)
    {
        for(int k=NN/10; a.a[i]<k; k/=10)printf("0");
        if(a.a[i])printf("%d",a.a[i]);
    }
    printf("\n");
}
void in(H &a,int x) //赋值
{
    if(!x)return;
    a.len=0;
    while(x)
    {
        a.a[++a.len]=x%NN;
        x=x/NN;
    }
}
bool bigD(H a,H b) //比较是否大于等于
{
    if(a.len>b.len)return 1;
    if(a.len<b.len)return 0;
    for(int i=a.len; i; i--)
        if(a.a[i]!=b.a[i])
            if(a.a[i]>b.a[i])return 1;
            else return 0;
    return 1;
}
H jia(H a,H b) //加法
{
    H c;
    int l=max(a.len,b.len);
    for(int i=1; i<=l; i++)
    {
        c.a[i]+=a.a[i]+b.a[i];
        c.a[i+1]=c.a[i]/NN;
        c.a[i]%=NN;
    }
    if(c.a[l+1])l++;
    c.len=l;
    return c;
}
H chu(H a) //除法
{
    H c;
    if(a.len==1)
    {
        c.a[1]=a.a[1]>>1;
        return c;
    }
    for(int i=a.len; i; i--)
    {
        if(a.a[i]&1ll)a.a[i-1]+=NN;
        c.a[i]=a.a[i]>>1;
    }
    if(c.a[a.len])c.len=a.len;
    else c.len=a.len-1;
    return c;
}
H rrr(H a) //-1操作
{
    if(a.len==1)
    {
        a.a[1]--;
        return a;
    }
    H c=a;
    c.a[1]--;
    int l=1;
    while(c.a[l]<0)
    {
        c.a[l]=NN-1;
        c.a[++l]--;
    }
    if(!c.a[c.len])c.len--;
    return c;
}
H chen(H a,H b) //乘法
{
    H z;
    z.len=a.len+b.len+2;
    for(int i=1; i<=a.len; i++)
        for(int j=1; j<=b.len; j++)z.a[i+j-1]+=(a.a[i]*b.a[j]);
    for(int i=1; i<=z.len; i++)z.a[i+1]+=z.a[i]/NN,z.a[i]%=NN; //取mo慢,放到外面取
    while(z.len>1&&!z.a[z.len])z.len--;
    return z;
}
H ksm(H a,int n) //快速幂
{
    if(n==1)return a;
    H c=ksm(a,n>>1);
    c=chen(c,c);
    if(n&1)c=chen(c,a);
    return c;
}
bool Chu(H a) //本来这个函数名是骂出题人的,怕和谐,就改了
{
    if(a.len*n-n+1>s.len)return 0;
    if(bigD(s,ksm(a,n)))return 1;
    return 0;
}
int main()
{
//    freopen("calc.in","r",stdin);
//    freopen("calc.out","w",stdout);
    scanf("%d",&n);
    init(s);
    if(n==1)
    {
        outit(s);
        return 0;
    }
    H l,r,mid,ans;
    in(l,1);
    r=s;
    while(bigD(r,l))
    {
        mid=jia(l,r);
        mid=chu(mid);
        if(Chu(mid))
        {
            if(bigD(mid,ans))ans=mid;
            H c;
            in(c,1);
            l=jia(mid,c);
        }
        else r=rrr(mid);
    }
    outit(ans);
}