005.cpp高精度

高精度运算

为什么需要高精度?

数据范围

int : 2,147,483,647

type 最小值 最大值 数量级
int -2^31 2^31-1 10^9
long long -2^63 2^63-1 10^18
unsigned long long 0 2^64-1 10^19
__int128 -2^127 2^127-1 10^38

超出数据范围的运算会得到错误的结果

比如两个int相加的结果如果超出2^31-1会溢出为负数

一般转成long long 就可解决

long long c=(long long)a+b;

但如果a+b大于2^127-1,那么我们用__int128也无法处理

甚至a,b本身都大于2^127-1,我们甚至无法储存这两个数

这时就需要引入高精度了

高精度数储存形式—倒序存储实现低位对齐

数组 a[ ]

规定 a[ 0 ] 存储数字a的位数

a[ 1 ]到a[ a[0] ]存储数字a在某进制下的一位,从低位到高位(个十百千……)

a[ a[0]+1 ]到最后都是0

也就是说我们是倒序存储的

以十进制为例

int a[SIZE];
memset(a,0,sizeof(int)*SIZE)
string num=2026;
a[0]=4;
a[1]=6,a[2]=2,a[3]=0,a[4]=2;

a[ a[0] ]即数字a的最高位

读入

常见场景是输入一个字符串表示一个高精度数

我们需要将它转成倒序的整形数组

const int SIZE=500;
void BigRead(int a[]){
    char s[SIZE];
    scanf("%s",s);//readline(s);
    int n=strlen(s);
    memset(a,0,sizeof(int)*SIZE);
    a[0]=n;
    for(int i=n;i;--i)
    a[i]=s[n-i]-'0';
}
    //int a[SIZE];
    //BigRead(a);

这样我们就读入了一个高精度数

输出

只要理解 a[ ] 的存储形式,输出很简单

void BigWrite(int a[]){
    for(int i=a[0];i;i--)putchar(a[i]+'0');
}

加法

由于我们的高精度数a[ ]、b[ ]是低位对齐的

只需从左到右模拟进位即可

c = a + b

void BigAdd1(int a[],int b[],int c[]){
    memset(c,0,sizeof(int)*SIZE);
    int i=1,ex=0;//进位
    for(;i<=a[0]||i<=b[0];++i){
        c[i]=a[i]+b[i]+ex;
        ex=c[i]/10;
        c[i]%=10;
    }
    c[i]=ex;
    c[0]=ex?i:i-1;
}

a += b

void BigAdd2(int a[],int b[]){
    int i=1,ex=0;
    for(;i<=a[0]||i<=b[0];i++){
        a[i]+=b[i]+ex;
        ex=a[i]/10;
        a[i]%=10;
    }
    a[i]=ex;
    a[0]=ex?i:i-1;
}

减法

高精度比大小

减法我们默认大减小

所以我们需要实现一个比较函数用于比较大小

  • 先比较位数
  • 位数一样就从高位到底位依次比较
bool BigCmp(int a[],int b[]){
    if(a[0]!=b[0])return a[0]>b[0];
    for(int i=a[0];i;--i)if(a[i]!=b[i])return a[i]>b[i];
    return 1;
}
  • a>=b 返回1
  • a < b 返回0

c=a-b

void BigSub1(int a[],int b[],int c[]){
    memset(c,0,sizeof(int)*SIZE);    
    c[0]=a[0];
    for(int i=1;i<=a[0];i++)c[i]=a[i];
    for(int i=1;i<=a[0];i++){
        if(i<=b[0])c[i]-=b[i];
            while(c[i]<0){
            c[i]+=10;
            c[i+1]--;
        }
    }
    while(c[0]>1&&c[c[0]]==0)c[0]--;
}

a-=b

void BigSub2(int a[],int b[]){
    int i=1;
    while(i<=b[0]){
        if(a[i]<b[i]){
            int j=i+1;
            while(j<=a[0]&&a[j]==0)a[j++]=9;
            a[j]--;
            a[i]+=10;
        }
        a[i]-=b[i];
        i++;
    }
    while (i<=a[0]&&a[i]<0){
        a[i]+=10;
        a[i+1]--;
        i++;
    }
    while(a[0]>1&&a[a[0]]==0)a[0]--;
}

乘法

高精度 × 小数

  • 从低到高、一边乘一边进位

c = a * b

void BigMulSamll1(int a[],int b,int c[]){
    memset(c,0,sizeof(int)*SIZE);
    if(b==0){
        c[0]=1;
        c[1]=0;
        return;
    }
    int ex=0;
    for(int i=1;i<=a[0];i++){
        c[i]=a[i]*b+ex;
        ex=c[i]/10;
        c[i]%=10;
    }
    int n=a[0];
    while(ex){c[++n]=ex%10;ex/=10;}
    c[0]=n;
} 

a *= b

void BigMulSamll2(int a[],int b){
    if(b==0){
        a[0]=1,a[1]=0;
        return;
    }
    int ex=0;
    for(int i=1;i<=a[0];i++){
        a[i]=a[i]*b+ex;
        ex=a[i]/10;
        a[i]%=10;
    }
    int n=a[0];
    while(ex){
        a[++n]=ex%10;
        ex/=10;
    }
    a[0]=n;
}

高精度 × 高精度

  • 枚举 a_i , b_ja_i * b_j 放入c_(i+j-1)
void BigMulBig(int a[],int b[],int c[]){
    memset(c,0,sizeof(int)*SIZE);
    for(int i=1;i<=a[0];++i){
        int ex=0;
        for(int j=1;j<=b[0];++j){
            c[i+j-1]=c[i+j-1]+a[i]*b[j]+ex;
            ex=c[i+j-1]/10;
            c[i+j-1]%=10;
        }
        int k=i+b[0];
        while(ex){
            c[k]+=ex;
            ex=c[k]/10;
            c[k]%=10;
            k++;
        }
    }
    int n=a[0]+b[0];
    while(c[n]==0&&n>1)n--;
    c[0]=n;
}

除法

高精度 ÷ 小数

  • 从高位到低位,将被除数添加到余数,并计算这一位的商,更新余数
void BigDivSamll(int a[],int b,int c[],int &re){
    memset(c,0,sizeof(int)*SIZE);
    long long t=0;
    int pos =a[0];
    for(int i=a[0];i>=1;i--){
        t=t*10+a[i];
        c[pos--]=t/b;
        t%=b;
    }
    int n=a[0];
    while(n>1&&c[n]==0)n--;
    c[0]=n;
    re=t;
}

高精度 ÷ 高精度

朴素累减

  • 从高位到低位,将被除数添加到余数
  • 余数大于除数时累减
void BigDivBig(int a[],int b[],int c[],int re[]){
    memset(c,0,sizeof(int)*SIZE);
    memset(re,0,sizeof(int)*SIZE);
    c[0]=a[0]-b[0]+1;
    for(int i=c[0];i;--i){
        int d[SIZE];
        memset(d,0,sizeof(d));
        memcpy(d+i,b+1,sizeof(int)*b[0]);
        d[0]=b[0]+i-1;
        while(BigCmp(a,d)){
            BigSub2(a,d);
            c[i]++;
        }
    }
    if(c[c[0]]==0)c[0]--;
    memcpy(re,a,sizeof(int)*SIZE);
}
  • 低进制下(100进制以下)我们只需累减即可
  • 对于更高的进制,二分试商的优势会越来越明显
  • 不过本文只讨论10进制下的高精度实现,不涉及二分试商

整合

namespace BIGINT{
    const int SIZE=500;
void BigRead(int a[]){
    char s[SIZE];
    scanf("%s",s);//readline(s);
    int n=strlen(s);
    memset(a,0,sizeof(int)*SIZE);
    a[0]=n;
    for(int i=n;i;--i)
    a[i]=s[n-i]-'0';
}
void BigWrite(int a[]){
    for(int i=a[0];i;i--)putchar(a[i]+'0');
}
void BigAdd1(int a[],int b[],int c[]){
    memset(c,0,sizeof(int)*SIZE);
    int i=1,ex=0;
    for(;i<=a[0]||i<=b[0];++i){
        c[i]=a[i]+b[i]+ex;
        ex=c[i]/10;
        c[i]%=10;
    }
    c[i]=ex;
    c[0]=ex?i:i-1;
}
void BigAdd2(int a[],int b[]){
    int i=1,ex=0;
    for(;i<=a[0]||i<=b[0];i++){
        a[i]+=b[i]+ex;
        ex=a[i]/10;
        a[i]%=10;
    }
    a[i]=ex;
    a[0]=ex?i:i-1;
}
bool BigCmp(int a[],int b[]){
    if(a[0]!=b[0])return a[0]>b[0];
    for(int i=a[0];i;--i)if(a[i]!=b[i])return a[i]>b[i];
    return 1;
}
void BigSub1(int a[],int b[],int c[]){
    memset(c,0,sizeof(int)*SIZE);    
    c[0]=a[0];
    for(int i=1;i<=a[0];i++)c[i]=a[i];
    for(int i=1;i<=a[0];i++){
        if(i<=b[0])c[i]-=b[i];
            while(c[i]<0){
            c[i]+=10;
            c[i+1]--;
        }
    }
    while(c[0]>1&&c[c[0]]==0)c[0]--;
}
void BigSub2(int a[],int b[]){
    int i=1;
    while(i<=b[0]){
        if(a[i]<b[i]){
            int j=i+1;
            while(j<=a[0]&&a[j]==0)a[j++]=9;
            a[j]--;
            a[i]+=10;
        }
        a[i]-=b[i];
        i++;
    }
    while (i<=a[0]&&a[i]<0){
        a[i]+=10;
        a[i+1]--;
        i++;
    }
    while(a[0]>1&&a[a[0]]==0)a[0]--;
}
void BigMulSamll1(int a[],int b,int c[]){
    memset(c,0,sizeof(int)*SIZE);
    if(b==0){
        c[0]=1;
        c[1]=0;
        return;
    }
    int ex=0;
    for(int i=1;i<=a[0];i++){
        c[i]=a[i]*b+ex;
        ex=c[i]/10;
        c[i]%=10;
    }
    int n=a[0];
    while(ex){c[++n]=ex%10;ex/=10;}
    c[0]=n;
} 
void BigMulSamll2(int a[],int b){
    if(b==0){
        a[0]=1,a[1]=0;
        return;
    }
    int ex=0;
    for(int i=1;i<=a[0];i++){
        a[i]=a[i]*b+ex;
        ex=a[i]/10;
        a[i]%=10;
    }
    int n=a[0];
    while(ex){
        a[++n]=ex%10;
        ex/=10;
    }
    a[0]=n;
}
void BigMulBig(int a[],int b[],int c[]){
    memset(c,0,sizeof(int)*SIZE);
    for(int i=1;i<=a[0];++i){
        int ex=0;
        for(int j=1;j<=b[0];++j){
            c[i+j-1]=c[i+j-1]+a[i]*b[j]+ex;
            ex=c[i+j-1]/10;
            c[i+j-1]%=10;
        }
        int k=i+b[0];
        while(ex){
            c[k]+=ex;
            ex=c[k]/10;
            c[k]%=10;
            k++;
        }
    }
    int n=a[0]+b[0];
    while(c[n]==0&&n>1)n--;
    c[0]=n;
}
void BigDivSamll(int a[],int b,int c[],int&re){
    memset(c,0,sizeof(int)*SIZE);
    int t=0;
    for(int i=a[0];i;--i){
        t=t*10+a[i];
        c[i]=t/b;
        t%=b;
    }
    re=t;
    int n=a[0];
    while(c[n]==0&&n>1)n--;
    c[0]=n;
}
void BigDivBig(int a[],int b[],int c[],int re[]){
    memset(c,0,sizeof(int)*SIZE);
    memset(re,0,sizeof(int)*SIZE);
    c[0]=a[0]-b[0]+1;
    for(int i=c[0];i;--i){
        int d[SIZE];
        memset(d,0,sizeof(d));
        memcpy(d+i,b+1,sizeof(int)*b[0]);
        d[0]=b[0]+i-1;
        while(BigCmp(a,d)){
            BigSub2(a,d);
            c[i]++;
        }
    }
    if(c[c[0]]==0)c[0]--;
    memcpy(re,a,sizeof(int)*SIZE);
}
}using namespace BIGINT;
压行
#include<bits/stdc++.h>
using namespace std;
namespace BIGINT{ int SIZE=10100;
void BigRead(int a[]){char s[SIZE];scanf("%s",s);int n=strlen(s);memset(a,0,sizeof(int)*SIZE);a[0]=n;for(int i=n;i;--i)a[i]=s[n-i]-'0';}
void BigWrite(int a[]){for(int i=a[0];i;i--)putchar(a[i]+'0');}
void BigAdd1(int a[],int b[],int c[]){memset(c,0,sizeof(int)*SIZE);int i=1,ex=0;for(;i<=a[0]||i<=b[0];++i){c[i]=a[i]+b[i]+ex;ex=c[i]/10;c[i]%=10;}c[i]=ex;c[0]=ex?i:i-1;}
void BigAdd2(int a[],int b[]){int i=1,ex=0;for(;i<=a[0]||i<=b[0];i++){a[i]+=b[i]+ex;ex=a[i]/10;a[i]%=10;}a[i]=ex;a[0]=ex?i:i-1;}
bool BigCmp(int a[],int b[]){if(a[0]!=b[0])return a[0]>b[0];for(int i=a[0];i;--i)if(a[i]!=b[i])return a[i]>b[i];return 1;}
void BigSub1(int a[],int b[],int c[]){memset(c,0,sizeof(int)*SIZE);c[0]=a[0];for(int i=1;i<=a[0];i++)c[i]=a[i];for(int i=1;i<=a[0];i++){if(i<=b[0])c[i]-=b[i];while(c[i]<0){c[i]+=10;c[i+1]--;}}while(c[0]>1&&c[c[0]]==0)c[0]--;}
void BigSub2(int a[],int b[]){int i=1;while(i<=b[0]){if(a[i]<b[i]){int j=i+1;while(j<=a[0]&&a[j]==0)a[j++]=9;a[j]--;a[i]+=10;}a[i]-=b[i];i++;}while (i<=a[0]&&a[i]<0){a[i]+=10;a[i+1]--;i++;}while(a[0]>1&&a[a[0]]==0)a[0]--;}
void BigMulSamll1(int a[],int b,int c[]){memset(c,0,sizeof(int)*SIZE);if(b==0){c[0]=1;c[1]=0;return;}int ex=0;for(int i=1;i<=a[0];i++){c[i]=a[i]*b+ex;ex=c[i]/10;c[i]%=10;}int n=a[0];while(ex){c[++n]=ex%10;ex/=10;}c[0]=n;}
void BigMulSamll2(int a[],int b){if(b==0){a[0]=1,a[1]=0;return;}int ex=0;for(int i=1;i<=a[0];i++){a[i]=a[i]*b+ex;ex=a[i]/10;a[i]%=10;}int n=a[0];while(ex){a[++n]=ex%10;ex/=10;}a[0]=n;}
void BigMulBig(int a[],int b[],int c[]){memset(c,0,sizeof(int)*SIZE);for(int i=1;i<=a[0];++i){int ex=0;for(int j=1;j<=b[0];++j){c[i+j-1]=c[i+j-1]+a[i]*b[j]+ex;ex=c[i+j-1]/10;c[i+j-1]%=10;}int k=i+b[0];while(ex){c[k]+=ex;ex=c[k]/10;c[k]%=10;k++;}}int n=a[0]+b[0];while(c[n]==0&&n>1)n--;c[0]=n;}
void BigDivSamll(int a[],int b,int c[],int &re){memset(c,0,sizeof(int)*SIZE);long long t=0;int pos =a[0];for(int i=a[0];i>=1;i--){t=t*10+a[i];c[pos--]=t/b;t%=b;}int n=a[0];while(n>1&&c[n]==0)n--;c[0]=n;re=t;}
void BigDivBig(int a[],int b[],int c[],int re[]){memset(c,0,sizeof(int)*SIZE);memset(re,0,sizeof(int)*SIZE);c[0]=a[0]-b[0]+1;for(int i=c[0];i;--i){int d[SIZE];memset(d,0,sizeof(d));memcpy(d+i,b+1,sizeof(int)*b[0]);d[0]=b[0]+i-1;while(BigCmp(a,d)){BigSub2(a,d);c[i]++;}}if(c[c[0]]==0)c[0]--;memcpy(re,a,sizeof(int)*SIZE);}
}using namespace BIGINT;

模板题检验

可用下面的题目检验模板

luogu p1601

luogu p2142

luogu p1303

luogu p1480

luogu p2005

简单运用

luogu p1009

luogu p1591

luogu p1249

posted @ 2025-12-10 22:26  射杀百头  阅读(14)  评论(0)    收藏  举报