C++中高精度模板全解析

高精度(不使用STL的OJ)

用字符串或字符数组输入,

用数组存储与运算,

用数组输出结果

高精度加法

#include<iostream>
#include<cstring>
using namespace std;
char s1[505],s2[505];
int a[505],b[505],c[505];
int main(){
    int m,n,v,t;
    std::ios::sync_with_stdio(false);//取消cin,cout与stdio的同步
    //如果使用文件输入输出的话,一定记住要把这条语句放在freopen()后面
    //输入
    cin>>s1>>s2;
    m=strlen(s1);
    n=strlen(s2);
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    //存储
    for(int i=0;i<m;i++){
        a[m-1-i]=s1[i]-'0';
    }
    for(int i=0;i<n;i++){
        b[n-1-i]=s2[i]-'0';
    }
    m=max(m,n);
    m++;
    memset(c,0,sizeof(c));
    //加法,进位运算
    for(int i=0;i<m;i++){         
        v=a[i]+b[i];
        if((c[i]+v)<10)
            c[i]+=v;
        else{
            c[i+1]+=(c[i]+v)/10;
            c[i]=(c[i]+v)%10;        
        }    
    }        

//下面是一种更简单的做法,结果直接储存在a中                    
    /*for(int i=0;i<m;i++){
        a[i]+=b[i];
        a[i+1]+=a[i]/10;
        a[i]%=10;    
    }*/
    if(c[m-1]==0)    m--;
    //输出
    for(int i=m-1;i>=0;i--)    cout<<c[i];
    return 0;
}

高精度减法

和高精度加法的基本思路相同,不过在减法中,需要确定输入数字的相对大小来判断是否输出负号,还需要注意是否要"借位"。

#include<iostream>
#include<cstring>
using namespace std;
//比较两个数相对大小 
bool compare(char s1[],char s2[]){
    int u=strlen(s1),v=strlen(s2);
    if(u!=v)
        return u>v;
    for(int i=0;i<u;i++)
        if(s1[i]!=s2[i])    return s1[i]>s2[i];
    return true;
}                                                  
int main(){
    std::ios::sync_with_stdio(false);
    int flag=1,i,j;
    char s1[100005],s2[100005],s3[100005];              //这里为节省空间考虑,直接用char数组运算 
    cin>>s1>>s2;
    if(compare(s1,s2));
    else{//被减数较小时
        flag=-1;
        strcpy(s3,s1);
        strcpy(s1,s2);
        strcpy(s2,s3);
    }
    int u=strlen(s1),v=strlen(s2);
     //从最后一位向前减 
    for(i=u-1,j=v-1;j>=0;i--,j--){
        if(s1[i]<s2[j]){
            s1[i-1]-=1;//借位
            s3[i]=s1[i]-s2[j]+10+'0';//+"0"确保返回的是字符串
        }
        else    s3[i]=s1[i]-s2[j]+'0';
    }                                                 
    for(;i>=0;i--)    {
        if(s1[i]<'0'){
            s1[i-1]-=1;
            s3[i]=s1[i]+10;
        }
        else    s3[i]=s1[i];
    }                                                  //s1数位大,所以还有s1减'0' 
    for(i=0;i<u-1&&s3[i]=='0';i++);                     //只到u-1的原因时 
    if(flag==-1)    cout<<'-';
    cout<<s3+i;
    return 0;
}

高精度乘法

#include<iostream>
#include<cstring>
using namespace std;
char a[2005],b[2005];
int c[2005],d[2005],e[4005];
int u,v,w;
int main(){
    int i,j;
    cin>>a>>b;
    u=strlen(a);
    v=strlen(b);
    memset(c,0,4005);
    for(int i=0;i<u;i++)
        c[u-1-i]=a[i]-'0';
    for(int i=0;i<v;i++)
        d[v-1-i]=b[i]-'0';
    for(i=0;i<u;i++){
        for(j=0;j<v;j++){
            w=c[i]*d[j];
            if(e[i+j]+w<10)    e[i+j]+=w;
            else{
                e[i+j+1]+=(e[i+j]+w)/10;
                e[i+j]=(e[i+j]+w)%10;
            }
        }
    }
    for(i=u+v-1;i>0&&e[i]==0;i--);            //此处是为了不漏掉输出结果为0而将条件写为i>0 
    for(;i>=0;i--)    cout<<e[i];
    return 0;
}

高精度除法

高精度除法分两种,都是仿照竖式除法实现,一种是高精度除以低精度,较为容易实现,主要是利用一个数,在线处理:

#include<iostream>
#include<cstring>
#include<string>
using namespace std;
char s1[5005];
int a[5005],b,c[5005],x=0;
int main(){
    cin>>s1>>b;
    a[0]=strlen(s1);
    for(int i=1;i<=a[0];i++)    a[i]=s1[i-1]-48;
    memset(c,0,sizeof(c));
    for(int i=1;i<=a[0];i++){
        c[i]=(x*10+a[i])/b;
        x=(x*10+a[i])%b;  
    }                              //核心部分
    x=1;
    while(c[x]==0&&x<a[0])    x++;
    for(;x<=a[0];x++)    cout<<c[x]; 
    return 0;
}

另一种,是运用逐次相减的方法确定出商和余数,代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int a[5005],b[5005],c[5005],d[5005];
bool compare(int a[],int b[]){
    if(a[0]!=b[0])    return a[0]>b[0];
    for(int i=a[0];i>0;i--){
        if(a[i]!=b[i])    return a[i]>b[i];
    }
    return true;
}
int main(){
    std::ios::sync_with_stdio(false);
    string s1,s2;
    cin>>s1>>s2;
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    a[0]=s1.length();
    b[0]=s2.length();
    for(int i=1;i<=a[0];i++)    a[i]=s1[a[0]-i]-'0';
    for(int i=1;i<=b[0];i++)    b[i]=s2[b[0]-i]-'0';
    c[0]=a[0]-b[0]+1;
    for(int i=c[0];i>0;i--){            //c[0]代表最初a,b的数位差 
        memset(d,0,sizeof(d));
        for(int j=1;j<=b[0];j++)
            d[j+i-1]=b[j];
        d[0]=b[0]+i-1;                           //先让b中存下的数与a在一个数量级 
        while(compare(a,d)){                    //比较,如果a比较大,用a直接减去d,如果a比较小,下一次大循环中,d的位数会减一 
            c[i]++;
            for(int i=1;i<=a[0];i++){
                a[i]-=d[i];
                if(a[i]<0){
                    a[i+1]--;
                    a[i]+=10;
                }
            }
            while(a[0]>0&&a[a[0]]==0)    a[0]--;
        }
    }
    while(c[0]>1&&c[c[0]]==0)    c[0]--;
    cout<<"商:";
    for(int i=c[0];i>0;i--)    cout<<c[i];
    cout<<endl<<"余数:";
    for(int i=a[0];i>0;i--)    cout<<a[i];           //a中最后剩下的就是余数  
    return 0;
}

高精度(可使用STL的OJ)

高精度加法

#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
vector<int> add(vector<int> &A,vector<int> &B)
{
    vector<int> C;
	int t=0;
	for(unsigned int i=0; i<A.size() || i<B.size();i++)
	{
		if(i<A.size()) t+=A[i];
		if(i<B.size()) t+=B[i];
		C.push_back(t%10);
		t/=10;
	}
	if(t) C.push_back(1);
	return C;
}
int main() {
    string a,b;
    vector<int> A,B;
    cin>>a>>b;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
    for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
    vector<int> C=add(A,B);
    for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}

高精度加法+乘法(面向对象+压位)

z[i]实际上只存储了一个0到9的数,这样就造成了空间浪费。我们不妨把十进制换成高进制,如一亿进制,这样就可以较充分地利用空间,另外还可以大大提高程序的运行速率。具体做法就是将程序中的 /10,%10 换成 /k,%k(k是进制)。
值得注意的是,若将十进制换成高进制,c.z[i+j]+=a.z[i]b.z[j] 这一步操作就有可能会爆空间。我们可以设一个long long型的v来记录c.z[i]+1lla.z[i]*b.z[i],通过v向前进位,有效避免爆空间的情况 。
另外,我们一定不能忽略补零操作。举个例子,当k=100000000时,c.z[i]中存储的是23,直接输出23是错误的,若23不在第一位,就应输出0000023 。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<string>
#include<algorithm>
using namespace std;
const int k=100000000;

struct gaojing{
	int z[100000];
	int l;
	gaojing(){
		memset(z,0,sizeof(z));
		l=1;
	}
	friend istream& operator>>(istream &cin,gaojing &v){
		static char s[100000];
		cin>>s;
		int l=strlen(s);
		reverse(s,s+l);//stl中的翻转函数 
		for(int a=l-1;a>=0;a--)
			v.z[a/8]=v.z[a/8]*10+s[a]-'0';
		v.l=l/8;
		if (l%8!=0) v.l++;
		return cin;
	}
	friend ostream& operator<<(ostream &cout,const gaojing &v){
		cout<<v.z[v.l-1];
		for(int i=v.l-2;i>=0;i--){//补零操作 
			if(v.z[i]<10000000)cout<<'0';
			if(v.z[i]<1000000)cout<<'0';
			if(v.z[i]<100000)cout<<'0';
			if(v.z[i]<10000)cout<<'0';
			if(v.z[i]<1000)cout<<'0';
			if(v.z[i]<100)cout<<'0';
			if(v.z[i]<10)cout<<'0';
			cout<<v.z[i];
		}
		return cout;
	}
};

gaojing operator+(const gaojing &a,const gaojing &b){
	gaojing c;
	int l=max(a.l,b.l);
	for(int i=0;i<l;i++){
		c.z[i]+=a.z[i]+b.z[i];
		c.z[i+1]+=c.z[i]/k;
		c.z[i]%=k;
	}
	if(c.z[l]!=0)l++;
	c.l=l;
	return c;
}

gaojing operator*(const gaojing &a,const gaojing &b){
	gaojing c;
	int l=a.l+b.l;
	for(int i=0;i<a.l;i++)
	    for(int j=0;j<b.l;j++){
	    	long long v=c.z[i]+1ll*a.z[i]*b.z[i];//1ll是将a.z[i]*b.z[i]转换成长整型 
	    	c.z[i+j+1]+=v/k;
	    	c.z[i+j]+=v%k;
		}
	for(int i=0;i<l;i++){
		c.z[i+1]+=c.z[i]/k;
		c.z[i]%=k;
	}
	while(l>0&&c.z[l]==0)
	    l--;
	l++;
	c.l=l;
	return c;
}

int main(){
	gaojing a,b;
	cin>>a>>b;
	cout<<a+b<<endl;
	cout<<a*b<<endl;
	return 0;
}

我们把十进制位中的每4~8位并在一起(笔者一般压4位,因为乘法时不会超过int的范围),然后照样加减,最后并不影响答案,但是要注意输出。对于除法,我们此时发现枚举104104到108108太慢了,注意到单调性,于是我们考虑二分试商,这样就也可以在log2BASElog2BASE的时间之内求出来了。
注意初始化、赋值和输出。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef int ll;
 
struct bign//big number
{
//init
    static const int BASE=(int)1e4,POW=4;
    static const int MAXLEN=20010;
    ll num[MAXLEN];//num[0]=length of the big number
    bign(int tp=0)
    {
        memset(num,tp,sizeof(num));
    }
    void clear()
    {
        memset(num,0,sizeof(num));
    }
    bool operator=(const char ch[])
    {
        int len=strlen(ch);
        for(int i=0;i<len;i++)
            num[(len-i-1)/POW+1]=num[(len-i-1)/POW+1]*10+ch[i]-'0';
        num[0]=(len-1)/POW+1;
    }
    
//compare
    //big number
    bool operator<(const bign &rsh)const
    {
        if(num[0]!=rsh.num[0])
            return num[0]<rsh.num[0];
        for(int i=num[0];i;i--)
        {
            if(num[i]!=rsh.num[i])
                return num[i]<rsh.num[i];
        }
        return 0;
    }
    //int/long long 
    ll max(ll _x,ll _y)
    {
        return _x>_y?_x:_y;
    }
    ll min(ll _x,ll _y)
    {
        return _x<_y?_x:_y;
    }
    
//operator
    //add
    void operator+=(const bign &rsh)
    {
        num[0]=max(num[0],rsh.num[0]);
        for(int i=1;i<=num[0];i++)
        {
            num[i]+=rsh.num[i];
            num[i+1]+=num[i]/BASE;
            num[i]%=BASE;
        }
        while(num[num[0]+1]>0)
            num[0]++;
    }
    bign operator+(const bign &rsh)const
    {
        bign res=*this;res+=rsh;
        return res;
    }
    //subtract
    void operator-=(const bign &rsh)
    {
        for(int i=1;i<=num[0];i++)
        {
            num[i]-=rsh.num[i];
            while(num[i]<0)
            {
                num[i]+=BASE;
                num[i+1]--;
            }
        }
        while(num[num[0]]<=0&&num[0]>0)
            num[0]--;
    }
    bign operator-(const bign &rsh)const
    {
        bign res=*this;res-=rsh;
        return res;
    }
    //multiply
    bign operator*(const bign &rsh)const
    {
        bign res;
        res.num[0]=num[0]+rsh.num[0]-1;
        for(int i=1;i<=num[0];i++)
            for(int j=1;j<=rsh.num[0];++j)
                res.num[i+j-1]+=num[i]*rsh.num[j];
        for(int i=1;i<=res.num[0];i++)
        {
            res.num[i+1]+=res.num[i]/BASE;
            res.num[i]%=BASE;
        }
        while(res.num[res.num[0]+1]>0)
        {
            res.num[0]++;
            res.num[res.num[0]+1]+=res.num[res.num[0]]/BASE;
            res.num[res.num[0]]%=BASE;
        }
        return res;
    }
    void operator*=(const bign &rsh)
    {
        bign res=*this;res=res*rsh;
        *this=res;
    }
    //divide
    void operator/=(const ll &rsh)
    {
        for(int i=num[0];i>1;i--)
        {
            num[i-1]+=(num[i]%rsh*BASE);
            num[i]/=rsh;
        }
        num[1]/=rsh;
        while(num[0]>0&&num[num[0]]<=0)
            num[0]--;
    }
    bign operator/(const ll &rsh)const
    {
        bign temp=*this ;
        temp/=rsh;
        return temp;
    }
    
    void operator/=(const bign &rsh)
    {
        bign l,r=*this,tmp_one;tmp_one="1";
        l.num[0]=1;
        while(l<r)
        {
            bign mid=(l+r+tmp_one)/2;
            if(*this<(rsh*mid))
                r=mid-tmp_one;
            else
                l=mid;
        }
        *this=l;
    }
    bign operator/(const bign &rsh)const
    {
        bign res=*this;res/=rsh;
        return res;
    }
    //mod
    void operator%=(const bign &rsh)
    {
        bign res=*this;
        res=res-res/rsh*rsh;
        *this=res;
    }
 
    bign operator%(const bign &rsh)const
    {
        bign res=*this;res%=rsh;
        return res;
    }
};
ostream&operator<<(ostream &out,const bign &x)
{
    printf("%d",x.num[x.num[0]]);
    for(int i=x.num[0]-1;i;i--)
        printf("%04d",x.num[i]);
    return out;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值