C++中超长整型类型的构造与实现(附源码)

来源:互联网 发布:大公教育知满天 编辑:程序博客网 时间:2024/05/22 13:38

在曾经的一次面试经历中,被问到这样的问题:如何处理长度有几十位甚至上百位整型数字的加减法?

这是个很有意思的问题,当时我并没能给出令人满意的答案,但这个问题一直围绕在我的脑海,至今,我想把它实现一下。其实这个问题的难度已经被有意的缩减了,因为这里只说到加减法,如果要用到乘除法,那可能会更为麻烦一点,这是其一。其二,没有涉及小数位。那么下面我们就先来实现超长整型的加减法吧。

(注:在写本篇学习笔记时,本人学习和参考了网络文章,并做了借鉴,感谢各位前辈的分享。如果本文对您有所帮助,您可以随意分享,如果发现文中有误,也请指教,谢谢。本文用到的调试工具:Microsoft Visual Studio 10,操作系统:Windows 7)

在进行加减法运算的时候,首先判断两个操作数的符号位,若为同号,则用加法,若为异号,则用减法。

为了满足基本类型中从个位开始相加的方法,首先将两个超长整型中的m_strData中的字符串反转,从字符串的第一位开始相加,最后又将结果反转还原。比如对于操作数“723456”+“76912”,首先反转为“654327”+“21967”,再从字符串的第一位相加,并且向右进位,得到结果为“863008”,最后反转还原为“800368”。

综上所述,我们需要的数据成员有:符号位、数据长度、数值。

需要用到的方法有:加法函数、减法函数、字符串反转函数,还需要重载“+”、“-”,还可以重载输入输出流“<<”、“>>”。

最后再加上一些需要用到的其他方法,头文件VeryLong.h的定义如下:

#pragma once#include <iostream>#include <string>#include <cstring>using namespace std;#define NUM_LEN 1000class CVeryLong{public:CVeryLong(void);CVeryLong(const char * sData);CVeryLong(const CVeryLong &other);~CVeryLong(void);int    getLen();int    getSign();char * getData();void   setData(const char * sData);static int  bigNum(const int a, const int b);/* 比较a、b大小 */static void plus(CVeryLong &cvl, CVeryLong &c1, CVeryLong &c2);static void sub(CVeryLong &cvl, CVeryLong &c1, CVeryLong &c2);friend CVeryLong operator+(CVeryLong &c1, CVeryLong &c2);friend CVeryLong operator-(CVeryLong &c1, CVeryLong &c2);friend istream &operator>>(istream &in, CVeryLong &c);friend ostream &operator<<(ostream &out, CVeryLong &c);private:void   reversalStr();/* 字符串反转 */void   setLen(const int &iLen);void   setSign(const int &iSign);int    m_iSign;    /* 符号位:1-负号 0-正号 */int    m_iLen;     /* 字符串长度 */char * m_sData;    /* 字符串值 */};

由于涉及正负数,所以两个操作数的相加在运算过程中不一定是相加,如果符号不一样,那就只能使用相减运算。这也是我把加减函数和重载运算符“+”、“-”分开的原因。

在做减法运算的之前,如果我们事先知道两个操作符的大小,那我们在运算的时候就会省很多事,所以我们希望在进入sub()函数之前,比较出大小,并且约定大在前小在后。

所以,重载运算符中的实现如下:

CVeryLong operator+(CVeryLong &c1, CVeryLong &c2){CVeryLong cvl;if (c1.getSign() == c2.getSign()){CVeryLong::plus(cvl,c1,c2);}else{if (c1.getLen()>c2.getLen()){CVeryLong::sub(cvl,c1,c2);}else if(c1.getLen()<c2.getLen()){CVeryLong::sub(cvl,c2,c1);}else{if (strcmp(c1.getData(),c2.getData()) > 0){CVeryLong::sub(cvl,c1,c2);}else if (strcmp(c1.getData(),c2.getData()) > 0){CVeryLong::sub(cvl,c2,c1);}else{cvl.setData("0");}}}return cvl;}
如开篇所说,在加减法运算过程中,我们可以先把操作数中的字符串反转,然后从第一位开始做加减运算,并且向右进位或者借位。在两个字符进行加减运算的时候,我们可以运用字符可自动转换成ASCII码直接进行加减,并且赋值给一个整形变量即可,当然我们知道0~9的ASCII码表对应的是48~57,所以要对操作数进行处理之后再做加减。

函数plus()的实现如下:

void CVeryLong::plus(CVeryLong &cvl, CVeryLong &c1, CVeryLong &c2){int  i=0;int  j=0;int  ver=0;char ch='0';char ch0='0';char ch1='\0';char ch2='\0';char str[NUM_LEN]={0};c1.reversalStr();c2.reversalStr();j=CVeryLong::bigNum(c1.getLen(),c2.getLen());for(i=0;(i<c1.getLen() || i<c2.getLen()) || (i==j && ch=='1');i++){if (i<c1.getLen()){ch1=c1.m_sData[i];}else{ch1='0';}if (i<c2.getLen()){ch2=c2.m_sData[i];}else{ch2='0';}ver=(ch1-ch0)+(ch2-ch0)+(ch-ch0);if(ver>9){ch='1';ver=ver-10;}else{ch='0';}sprintf_s(str,NUM_LEN,"%s%d",str,ver);}cvl.setData(str);cvl.setSign(c1.getSign());cvl.reversalStr();c1.reversalStr();c2.reversalStr();}


函数sub()的实现如下:

void CVeryLong::sub(CVeryLong &cvl, CVeryLong &c1, CVeryLong &c2){int  i=0;int  j=0;int  ver=0;char ch='0';char ch0='0';char ch1='\0';char ch2='\0';char str[NUM_LEN]={0};c1.reversalStr();c2.reversalStr();j=CVeryLong::bigNum(c1.getLen(),c2.getLen());for(i=0;(i<c1.getLen() || i<c2.getLen());i++){if (i<c1.getLen()){ch1=c1.m_sData[i];}else{ch1='0';}if (i<c2.getLen()){ch2=c2.m_sData[i];}else{ch2='0';}ver=(ch1-ch0)-(ch2-ch0)-(ch-ch0);if(ver<0){ch='1';ver=ver+10;}else{ch='0';}sprintf_s(str,NUM_LEN,"%s%d",str,ver);}cvl.setData(str);cvl.setSign(c1.getSign());cvl.reversalStr();c1.reversalStr();c2.reversalStr();}


至此,一些主要函数的实现都已经在上面了,最后,贴出辅助函数的实现。

函数reversalStr()的实现如下:

void CVeryLong::reversalStr(){char c;for(int i=0;i<m_iLen/2;++i){c=m_sData[i];m_sData[i]=m_sData[m_iLen-i-1];m_sData[m_iLen-i-1]=c;}}


函数setData()的实现如下:

void   CVeryLong::setData(const char * sData){if (sData==m_sData){return;}if (NULL != m_sData){delete []m_sData;}m_iSign=0;m_iLen=0;const char * tmp=NULL;if (NULL == sData){m_iSign=0;m_iLen=0;m_sData=new char[1];*m_sData='\0';}else{tmp=sData;if (strncmp(sData,"-",1)==0){m_iSign=1;sData++;}else if (strncmp(sData,"+",1)==0){m_iSign=0;sData++;}m_iLen=strlen(sData);m_sData=new char[m_iLen+1];strcpy(m_sData,sData);sData=tmp;}}


重载运算符函数ostream &operator<<()的实现如下:

 ostream &operator<<(ostream &out, CVeryLong &c) { if (c.getSign() == 1) { out<<"-"; } out<<c.m_sData; return out; }


三个构造函数的定义如下:

CVeryLong::CVeryLong(void){m_iSign=0;m_iLen=0;m_sData=new char[1];*m_sData='\0';}
CVeryLong::CVeryLong(const char *sData){const char * tmp=NULL;if (NULL == sData){m_iSign=0;m_iLen=0;m_sData=new char[1];*m_sData='\0';}else{tmp=sData;if (strncmp(sData,"-",1)==0){m_iSign=1;sData++;}else if (strncmp(sData,"+",1)==0){m_iSign=0;sData++;}else{m_iSign=0;}m_iLen=strlen(sData);m_sData=new char[m_iLen+1];strcpy(m_sData,sData);sData=tmp;}}
CVeryLong::CVeryLong(const CVeryLong &other){if (&other==this){return;}m_iSign=other.m_iSign;m_iLen=other.m_iLen;m_sData=new char[m_iLen+1];strcpy(m_sData,other.m_sData);}


析构函数的实现如下:

CVeryLong::~CVeryLong(void){m_iSign=0;m_iLen=0;delete []m_sData;}
















1 0