一个很快又不那么长的高精度模板

模拟赛竟然出高精度题,我竟然没有高精度板子。

所以就学了一些高精度相关知识,写了一个高精度板子。

因为我发现我搜到的高精度板子都很长,甚至很多在洛谷上交都提示代码过长,所以我就想着自己整一个又短又好用的高精度板子。

总体优化主要有以下几点:

  1. FFT 实现了标准 \(\mathcal O(n \log n)\) 的大整数乘法。
  2. 牛顿迭代法实现的 \(\mathcal O(n \log n)\) 的大整数除法‘。
  3. \(3\) 位存储高精度整数,常数小。

有多快?

  • 十进制长度在 \(10^6\) 级别的大整数加法耗时约 \(10\operatorname{ms}\)
  • 十进制长度在 \(10^6\) 级别的大整数乘法耗时约 \(200\operatorname{ms}\)
  • 十进制长度在 \(10^6\) 级别的大整数除法耗时约 \(1.5\operatorname{s}\)

同时,本模板使用 std::vector 动态分配内存,避免内存浪费或内存溢出。

代码长度并不长,不到 10 KB,300 行左右,比起动辄一千行的板子还是短很多的。

$\color{00DD33} {模板代码}\small\color{445566}{(已封装类 int\_t)}$
#include<vector>
#include<cmath>
#include<stdint.h>
#include<string>
#include<stdexcept>
#include <iostream>
using namespace std;
/**
 * @brief An efficient C++ arbitrary-precision integer arithmetic.
 * @author [Berd__](https://www.luogu.com.cn/user/959419)
 * @date 2026-8-24
 * Before using:
 * 1. add "using namespace std;" to the start of the code.
 * 2. add header files below,or add "#include<bits/stdc++.h>"
 *    to the start of the code.
 *      #include<vector>
 *      #include<cmath>
 *      #include<stdint.h>
 *      #include<string>
 *      #include<stdexcept>
 * 3. This is NOT a completed template,if you meet any problem,
 *    please contact [Berd__](https://www.luogu.com.cn/user/959419).
**/
/// @brief bigint moudle
struct int_t {
    vector<int16_t> num;
    bool sign;
    static constexpr int32_t BASE=1000;
    static constexpr int32_t BASE_DIGITS=3;
    const double PI=acos(-1.0);
    char to_char(const int &x){ return (x<10?('0'+x):('A'+x)); }
    void normalize(){
        while(!num.empty()&&num.back()==0) num.pop_back();
        if(num.empty()) sign=1;
    }
    int_t operator=(int64_t b){// this<-int
        num.clear();
        sign=(b>=0),b=b>0?b:-b;
        while(b>0){
            num.push_back(b%BASE);
            b/=BASE;
        }
        return (*this);
    }
    int_t operator=(const int_t &b){// this<-int_t
        num=b.num,sign=b.sign;
        return (*this);
    }
    int_t()         {sign=1,num.clear(); }
    int_t(int64_t b){(*this)=b;          }
    int_t(string b) {(*this).from_dec(b);}
    int_t(vector<int16_t> a,bool sig){ (*this).num=a,(*this).sign=sig; }
    bool operator==(int_t &b){
        normalize(),b.normalize();
        return (sign==b.sign&&num==b.num);
    }
    bool operator<(int_t &b){
        normalize(),b.normalize();
        if(num.empty()&&b.num.empty()) return 0;
        if(num.empty()) return !b.sign;
        if(b.num.empty()) return !sign;
        if(sign!=b.sign) return !sign;
        if(num.size()!=b.num.size()) return (num.size()>b.num.size())^sign;
        for(int i=num.size()-1;i>=0;i--){
            if(num[i]==b.num[i]) continue;
            else if(num[i]<b.num[i]) return sign;
            else return !sign;
        }
        return 0;
    }
    bool operator>(const int_t &b)const{
        if(num.empty()&&b.num.empty()) return 0;
        if(num.empty()) return !b.sign;
        if(b.num.empty()) return sign;
        if(sign!=b.sign) return sign;
        if(num.size()!=b.num.size()) return !((num.size()>b.num.size())^sign);
        for(int i=num.size()-1;i>=0;i--){
            if(num[i]==b.num[i]) continue;
            else if(num[i]>b.num[i]) return sign;
            else return !sign;
        }
        return 0;
    }
    bool operator<=(int_t &b){ return ((*this)<b||(*this)==b); }
    bool operator>=(int_t &b){ return ((*this)>b||(*this)==b); }
    int_t abs()      const{ return {num,1};                        }
    int_t operator-()const{ return num.empty()?0:int_t(num,!sign); }
    int_t operator<<(const int &k){//WARNING: This is NOT a standard operator,it returns this*(BASE^k)
        if(num.empty()||k==0) return (*this);
        int_t res=(*this);
        res.num.insert(res.num.begin(),k,0);
        res.normalize();
        return res;
    }
    int_t operator>>(const int &k){//WARNING: This is NOT a standard operator,it returns this/(BASE^k)
        if(num.empty()||k<=0) return (*this);
        if(k>=(int)num.size()) return 0;
        int_t res;
        res.sign=sign;
        res.num.assign(num.begin()+k,num.end());
        res.normalize();
        return res;
    }
    
    int_t operator+(int_t b)const{//returns the sum of two int_t numbers.
        int_t res;
        if(sign==b.sign){
            int maxn=max(num.size(),b.num.size());
            int carry=0;
            for(int i=0;i<maxn;i++){
                int qwq=(i>=num.size()?0:num[i])+(i>=b.num.size()?0:b.num[i])+carry;
                if(qwq>=BASE) qwq=qwq-BASE,carry=1;
                else carry=0;
                res.num.push_back(qwq);
            }
            if(carry) res.num.push_back(carry);
            return int_t(res.num,sign);
        }
        else{
            int_t aa=this->abs(),bb=b.abs();
            if(aa==bb) return 0;
            if(aa>bb) res.sign=sign;
            else res.sign=b.sign,aa=bb,bb=(*this).abs();
            int borrow=0;
            size_t maxn=aa.num.size();
            for(int i=0;i<maxn;i++){
                int qwq=aa.num[i]-(i<bb.num.size()?bb.num[i]:0)-borrow;
                if(qwq<0) qwq+=BASE,borrow=1;
                else borrow=0;
                res.num.push_back(qwq);
            }
            while(!res.num.empty()&&res.num.back()==0) res.num.pop_back();
            return res;
        }
    }
    int_t operator-(const int_t &b)const{ return (*this)+(-b); }
    int_t operator+=(const int_t &b){ return (*this)=(*this)+b;}
    int_t operator-=(const int_t &b){ return (*this)=(*this)-b;}
    int_t operator++(){ return (*this)+=1; }
    int_t operator--(){ return (*this)-=1; }
    struct Complex {
        double r=0,i=0;
        Complex operator=(const pair<double,double> &b){ r=b.first,i=b.second;return *this; }
        Complex operator+(const Complex &b)const{ return {r+b.r,i+b.i}; }
        Complex operator-(const Complex &b)const{ return {r-b.r,i-b.i}; }
        Complex operator*(const Complex &b)const{ return {r*b.r-i*b.i,r*b.i+i*b.r}; }
    };
    int lim,L;
    vector<int32_t> rev;
    void init_FFT(const int n){
        lim=1,L=0;
        while(lim<n)lim <<= 1,L++;
        rev.resize(lim+1);
        if(L==0){
            rev[0]=0;
            return;
        }
        for(int i=0;i<lim;++i)
            rev[i]=((rev[i>>1]>>1)|((i&1)<<(L-1)));
    }
    void FFT(Complex *f,int op){// Fast-Fast-TLE
        for(int i=0;i<lim;i++){
            if(i>rev[i]-1)continue;
            swap(f[i],f[rev[i]]);
        }
        for(int mid=1;mid<lim;mid<<=1){
            Complex wn={cos(PI/mid),op*sin(PI/mid)};
            for(int R=mid<<1,j=0;j<lim;j+=R){
                Complex w={1,0};
                for(int k=0;k<mid;k++,w=w*wn){
                    Complex y=f[j+k],z=w*f[j+mid+k];
                    f[j+k]=y+z;
                    f[j+mid+k]=y-z;
                }
            }
        }
    }
    int_t operator*(const int_t &b){//multiply two int_t numbers.
        // printf("%.20Lf",PI);
        int_t ans;
        ans.sign=!(sign^b.sign);
        int n=num.size(),m=b.num.size();
        if((*this).num.size()==0||b.num.size()==0) return 0;
        int siz=n+m-1;
        init_FFT(siz);
        vector<Complex> aa(lim),bb(lim);
        for(int i=0;i<n;i++) aa[i].r=num[i];
        for(int i=0;i<m;i++) bb[i].r=b.num[i];
        FFT(aa.data(),1),FFT(bb.data(),1);
        for(int i=0;i<lim;i++) aa[i]=aa[i]*bb[i];
        FFT(aa.data(),-1);
        // for(int i=0;i<aa.size();i++) printf("%.2Lf\n",aa[i].r);
        vector<int64_t> res(siz);
        int64_t carry=0;
        for(int i=0;i<siz;i++){
            int64_t qwq=(int64_t)(aa[i].r/lim+0.5L)+carry;
            res[i]=qwq%BASE;
            carry=qwq/BASE;
        }
        while(carry) res.push_back(carry%BASE),carry/=BASE;
        while(!res.empty()&&res.back()==0) res.pop_back();
        // for(int i=0;i<res.size();i++) cout<<res[i]<<endl;
        ans.num.resize(res.size());
        for(int i=0;i<res.size();i++) ans.num[i]=res[i];
        return ans;
    }
    int_t operator*(int64_t b)const{//multiply an int_t number and a normal number.
        if(num.empty()||b==0) return 0;
        int_t res;
        res.sign=sign^(b<0);
        b=(b>0?b:-b);
        int64_t carry=0;
        for(int i=0;i<num.size();i++){
            int64_t cur=(int64_t)num[i]*b+carry;
            res.num.push_back(cur%BASE);
            carry=cur/BASE;
        }
        while(carry){
            res.num.push_back(carry%BASE);
            carry/=BASE;
        }
        res.normalize();
        return res;
    }
    int_t reciprocal_impl(int p){//return floor(BASE^p/this),this is normalized.
        normalize();
        int_t a=(*this);
        int m=num.size();
        if(p<m) return 0;
        if(p<=m+1){
            int_t pow=int_t(1)<<p;
            int64_t low=0,high=1;
            for(int i=0;i<p-m+1;i++) high*=BASE;
            high++;
            while(low+1<high){
                int64_t mid=(low+high)>>1;
                if(a*int_t(mid)<=pow) low=mid;
                else high=mid;
            }
            return low;
        }
        int h=(m+p+1)>>1;
        int_t x0=reciprocal_impl(h)<<(p-h);
        int_t u=(int_t(2)<<p)-a*x0;
        int_t x1=(x0*u)>>p;
        int_t one=1,pow10=one<<p;
        while((x1+one)*a<=pow10) x1+=one;
        while(x1*a>pow10) x1-=one;
        return x1;
    }
    int_t reciprocal(int p){
        (*this).normalize();
        if(num.empty()) throw runtime_error("Runtime error: int_t divided by zero.");
        return reciprocal_impl(p);
    }
    int_t div_abs(int_t b){
        (*this).normalize(),b.normalize();
        int n=num.size(),m=b.num.size();
        if(n<m) return 0;
        int p=n;
        int_t Rev=b.reciprocal(p);
        int_t Q0=((*this)*Rev)>>p,one=1;
        while((Q0+one)*b<=(*this)) Q0+=one;
        while(Q0*b>(*this)) Q0-=one;
        Q0.normalize();
        return Q0;
    }
    int_t operator/(int_t b){
        if(num.empty()) return 0;
        int_t Q=(this->abs()).div_abs(b.abs());
        Q.sign=!(sign^b.sign);
        Q.normalize();
        return Q;
    }
    int_t operator%(const int_t &b)const{
        if(num.empty()) return 0;
        int_t Q=(this->abs()).div_abs(b.abs());
        int_t R=(*this).abs()-Q*(b.abs());
        R.sign=sign;
        R.normalize();
        return R;
    }
    int_t operator/=(const int_t &b){ return (*this)=(*this)/b; }
    int_t operator%=(const int_t &b){ return (*this)=(*this)%b; }
    int_t from_dec(string s){//this<-- a dec string
        (*this)=0;
        if(!s.empty()&&s[0]=='-') sign=0,s.erase(0,1);
        while(!s.empty()&&s[0]=='0') s.erase(0,1);
        if(s.empty()) return (*this)=0;
        int n=s.size(),curnum=0;
        int m=n-((int)(n/BASE_DIGITS))*BASE_DIGITS;
        for(int i=n-1;i-BASE_DIGITS+1>=m;i-=BASE_DIGITS){
            for(int j=i-BASE_DIGITS+1;j<=i;j++){
                curnum*=10;
                curnum+=(s[j]-'0');
            }
            num.push_back(curnum);
            curnum=0;
        }
        for(int i=0;i<m;i++){
            curnum*=10;
            curnum+=(s[i]-'0');
        }
        if(m>0) num.push_back(curnum);
        return (*this);
    }
    string to_dec(){//returns a dec string of the number.
        if(num.size()==0) return "0";
        string res=(sign?"":"-")+to_string(num.back());
        for(int i=(int)num.size()-2;i>=0;i--){
            string part=to_string(num[i]);
            res+=string(BASE_DIGITS-part.size(),'0')+part;
        }
        return res;
    }
    friend ostream& operator<<(ostream &out,int_t a){
        return out<<a.to_dec();
    }
    friend istream& operator>>(istream &in,int_t &a){
        string s;
        in>>s,a.from_dec(s);
        return in;
    }
};

能干什么?

以下功能除特殊说明外,支持高精度类型与普通整数类型直接运算,

所有运算除特殊说明外运算规则同普通十进制整数。

以下复杂度中的 \(n\) 除特殊说明外均为高精度数十进制下的长度。

由于是压位高精,所有运算的复杂度都带有 \(\frac{1}{3}\)\(\frac{1}{9}\) 的常数,这使得运算更快。


功能列表(目前支持)

\(\color{red}{不在列表中的函数/运算符是为其他函数保留使用或未定义的,\newline擅自调用可能引发错误行为。}\)

运算符/函数 用法 功能 复杂度 备注
normalize() a.normalize() 去前导 0 \(\mathcal O(1)\) 一般没啥用,\(\newline\)运算过程中都会去前导零
= a = b 赋值 \(\mathcal O(n)\)
== < > <= >= a < b 比较大小 \(\operatorname O(n)\)
abs() a.abs() 返回高精度\(\newline\)数的绝对值 \(\mathcal O(1)\)
- -a 返回当前\(\newline\)数字相反数 \(\mathcal O(1)\)
<< >> a<<b a>>b 返回 \(a\cdot base^{b}\) \(\newline\)\(\frac{a}{base^{b}}\) \(\mathcal O(n)\) \(base=10^3\)\(\newline\)与平常的左移/右移\(\newline\)结果不同,一般不要调用
+ - a+b a-b 返回两个数\(\newline\)的和/差 \(\mathcal O(n)\) 复杂度中的 \(n\)\(\newline\)两个参与运算的数\(\newline\)长度最大值
+= -= a+=b a-=b \(a\leftarrow a+b\) \(\mathcal O(n)\) ^
++ -- a++ a-- \(a\leftarrow a+1\) \(\mathcal O(1)\) 当需要处理进位时为 \(\operatorname O(n)\),但平均复杂度约等于 \(\mathcal O(1)\)
* a*b 返回两个数\(\newline\)的积 \(\mathcal O(n \log n)\) 复杂度中的 \(n\) 为两个\(\newline\)参与运算 的数长度之和
/ a/b 返回两个数\(\newline\)的商 \(\mathcal O(n \log^2 n)\) 复杂度中的 \(n\) 为两个\(\newline\)参与运算的数长度之和,\(\newline\)常数略大
% a%b 返回两个数\(\newline\)相除的余数 \(\mathcal O(n \log^2 n)\) ^
*= /= %= a%b 乘除/取模\(\newline\)并赋值 \(\mathcal O(n \log^2 n)\) ^
from_dec(string s) a.from_dec() 十进制字符串\(\newline\)转高精度并赋值 \(\mathcal O(n)\)
to_dec() a.to_dec() 当前高精度整数\(\newline\)转为十进制字符串 \(\mathcal O(n)\)
<< >> cin>>a cout<<a cin/cout \(\newline\)输入/输出 \(\mathcal O(n)\)

计划添加的功能

  • 进制转换
  • 更快的乘除法(使用 NTT,虽然现在我还不会 NTT)
  • 二进制按位运算符
  • 开方运算
  • 高精度小数
  • 咕咕咕……
posted @ 2026-08-24 09:29  _Berd  阅读(10)  评论(0)    收藏  举报