用C++实现Bigintger类

前几天受到Java中Biginteger类的启发,自己用C++ 写了一个大整数类,目前功能还很不完善,只支持加、减、乘、比较大小而且不支持负数。代码如下:

/*注意点
 *1.数字存储方式:data[0]为最低位,往后依次升高
 *2.注意0的特殊处理
 */
#ifndef BIGINT_H
#define BIGINT_H
#include <cstring>
#include <string>
#include <algorithm>  //std::reverse
#include <cmath>
#include "Exceptions.h"
#define NUM_GROUP_SIZE 4
#define MOD_BASE 10000
using std::string;
using std::reverse;
using std::min;
using std::max;
using std::bad_exception;

class Biginteger{
  private:
    int *data;
    int eff_len;  //有效长度
    inline bool check(const char *s,int len) {
        for(int i=0; i<len; i++) {
            if(!(s[i]>='0'&&s[i]<='9'))    return 0;
        }
        return 1;
    }
    /*删除前导0*/
    inline int firNoneZero(const char *s,int len) {
        int i;
        for(i=0; i<len-1&&s[i]=='0'; i++);
        return i;
    }
    /*转换为int数组,方便乘法与加法运算*/
    inline void convert(const char *s,int len) {
        int k=0;
        for(int i=len-1; i>=0; i-=NUM_GROUP_SIZE) {
            int now=0;
            for(int j=i-(NUM_GROUP_SIZE-1); j<=i; j++) {
            	if(j<0)	   continue;
                now=now*10+(s[j]-'0');
            }
            data[k++]=now;
        }
    }
    /*处理加法剩下的位数。a为位数较多的数*/
    inline void Add_lefts(const Biginteger &a,Biginteger &res,bool &carry,int minlen) {
        for(int i=minlen; i<a.eff_len; i++) {
            int now=a.data[i]+carry;
            res.data[res.eff_len++]=now%MOD_BASE;
            carry=(bool)(now/MOD_BASE);
        }
    }
    /*求长度*/
    inline void make_length(Biginteger &a) {
        int highest=a.data[a.eff_len-1];
		if(a.eff_len==1&&highest==0)  a.length=1;
        else   a.length=(a.eff_len-1)*NUM_GROUP_SIZE+(int)log10(highest)+1;
    }
    /*处理减法。在此处提供统一接口是因为结果可正可负*/
    Biginteger general_subt(const Biginteger &large,const Biginteger &small) {
        bool tw=0;  //退位
        Biginteger ret(large.length);
        for(int i=0; i<small.eff_len; i++) {
            int now=large.data[i]-small.data[i]-tw;
            if(now<0) {
                tw=1;
                now=MOD_BASE-abs(now);
            }
            else    tw=0;
            ret.data[ret.eff_len++]=now%MOD_BASE;
        }//处理后面几位
        for(int i=small.eff_len; i<large.eff_len; i++) {
            int now=large.data[i]-tw;
            if(now<0) {
                tw=1;
                now=MOD_BASE-abs(now);
            }
            else    tw=0;
            ret.data[ret.eff_len++]=now%MOD_BASE;
        }
        //前导零的特殊处理。注意a==b的特殊情况
        //注意:由于上面统一用位数较多数的位数初始化,所以此处可能不知要删1个
        while(ret.data[ret.eff_len-1]==0&&ret.eff_len>1)    ret.eff_len--;
        make_length(ret);
        return ret;
    }
  public:
    int length; //长度
    Biginteger():data(NULL),eff_len(0),length(0) {}
    
    Biginteger(const char *s) {
        if(s==NULL) throw bad_exception();
        int len=strlen(s);
        if(!check(s,len))    throw NumberFormatException();
        int loc=firNoneZero(s,len);
        length=len-loc;
        eff_len=ceil(length/(double)NUM_GROUP_SIZE);
        data=new int[eff_len]();
        if(strcmp(s,"0")==0)	data[0]=0;
        //注 长度不是原来的字符串长度了
        else    convert(s+loc,length);
    }
   
    Biginteger(string str) {
        char *S=new char[str.length()+1]();
        strcpy(S,str.c_str());
        //placement new
        new(this) Biginteger(S);
    }
   
    Biginteger(int len):eff_len(0),length(0) {
        data=new int[(int)ceil(len/(double)NUM_GROUP_SIZE)]();
    }
      //拷贝构造函数
    Biginteger(const Biginteger &another) {
        eff_len=another.eff_len;
        length=another.length;
        data=new int[another.eff_len]();
        for(int i=0; i<eff_len; i++)    data[i]=another.data[i];
    }
    
    Biginteger& operator=(const Biginteger &another) {
        if (this != &another) {  //加上判断,防止自己赋给自己
            if (data != NULL)    delete data;  //如果原来有数据,则释放数据
            eff_len = another.eff_len;
            length = another.length;
            data = new int[another.eff_len]();
            for (int i = 0; i < eff_len; i++)    data[i] = another.data[i];
        }
        return *this;
    }
    /*比较大小。注意从高位向低位比。*/
    inline int compareTo(const Biginteger &a) {
        int alen=a.eff_len;
        if(eff_len>a.eff_len)    return 1;
        if(eff_len<a.eff_len)    return -1;
        for(int i=alen-1; i>=0; i--) {
            if(data[i]>a.data[i])   return 1;
            else if(data[i]<a.data[i])    return -1;
        }
        return 0;
    }
    bool operator<(const Biginteger &a)  {return this->compareTo(a)==-1;}
    bool operator>(const Biginteger &a)  {return this->compareTo(a)==1;}
    bool operator>=(const Biginteger &a) {return this->compareTo(a)>=0;}
	bool operator<=(const Biginteger &a) {return this->compareTo(a)<=0;}
    bool operator==(const Biginteger &a) {return this->compareTo(a)==0;}
    bool operator!=(const Biginteger &a) {return this->compareTo(a)!=0;}
    Biginteger Add(const Biginteger &a) {
		int minlen=min(eff_len,a.eff_len);
		bool carry=0;  //进位标记
		Biginteger ret(max(length,a.length)+1);  //待返回结果
		for(int i=0; i<minlen; i++) {
            int now=data[i]+a.data[i]+carry;
            ret.data[ret.eff_len++]=now%MOD_BASE;
            carry=(bool)(now/MOD_BASE);
		}
		if(minlen==eff_len)  Add_lefts(a,ret,carry,minlen);
		else    Add_lefts(*this,ret,carry,minlen);
		//处理多余进位
		if(carry!=0)   ret.data[ret.eff_len++]=1;
        make_length(ret);
		return ret;
    }/*注意:拷贝构造函数的引用必须为const引用,因为返回值(拷贝)的函数的结果不能用于参数为普通引用类型的参数*/
    Biginteger operator+(const Biginteger &a) {return Add(a);}
    
    Biginteger Subt(const Biginteger &a) {
        if(compareTo(a)>=0) {
            Biginteger ret(general_subt(*this,a));
            return ret;
        }
        else {
            Biginteger ret(general_subt(a,*this));
            ret.data[ret.eff_len-1]=-ret.data[ret.eff_len-1];
            return ret;
        }
    }
    Biginteger operator-(const Biginteger &a) {return Subt(a);}
    
    Biginteger Multiply(const Biginteger &a) {
        int aLen=a.eff_len;
        Biginteger ret(length+a.length);  //存放结果
        for(int i=0; i<eff_len; i++) {  //本数
            int carry=0;
            for(int j=0; j<aLen; j++) {  //a
                int now=data[i]*a.data[j]+carry+ret.data[i+j];
                carry=now/MOD_BASE;
                ret.data[i+j]=now%MOD_BASE;
            }
            if(carry!=0)    ret.data[i+aLen]=carry;
        }
        ret.eff_len=(int)ceil((length+a.length)/(double)NUM_GROUP_SIZE);  //?
        //去除前导0
        /*if*/while(ret.data[ret.eff_len-1]==0&&ret.eff_len!=1)    ret.eff_len--;
		make_length(ret);
        return ret;
    }
    Biginteger operator*(const Biginteger &a) {return Multiply(a);}
    
    const string toString() {
    	if(data[0]==0&&eff_len==1)    return "0";
    	string ret="";
		int now=0;
		for(int i=0; i<eff_len-1; i++) {
			int now=data[i];
			for(int j=0; j<NUM_GROUP_SIZE; j++) {
				ret.append(1,(now%10)+'0');
				now/=10;
			}
		}
		now=abs(data[eff_len-1]);
		//处理最高位
		while(now>0) {
			ret.append(1,(now%10)+'0');
			now/=10;
		}
		//处理减法结果为负数的情况
		if(data[eff_len-1]<0)	ret+="-";
		reverse(ret.begin(),ret.end());
		return ret;
    }
    ~Biginteger() {
		if(data!=NULL) {
    		delete []data;
    		data=NULL;
		}
	}
};
#endif // BIGINT_H

实现过程中遇到一些问题,在此总结:

  1. 数据存储问题:始终记住data[0]是最低位,往后位数依次升高。
  2. 转为字符串时的问题:如果将data[]的内容转为C风格字符串,需注意不要用一个std::string记录结果最后再return ret.c_str();,因为ret被析构之后转换的指向C风格字符串的指针也不再有效。用string构造也应这样。我采取的是直接转换为std::string。
  3. 接上,转换时除最高位外都要补前导0。转换后,字符串整体进行反转。
  4. 减法和乘法都可能不止出现一个前导0。
  5. 析构函数问题:个人认为比较顽固和隐蔽的一个。为一个Biginteger对象用赋值运算符 = 会出错(调试输出SIGTRAP),经检查是一个数据被析构两次。问题的根源在于采用=赋值会导致浅拷贝,因此重载这个运算符即可。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值