您的位置:首页 > 编程语言 > C语言/C++

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

2014-08-02 17:32 344 查看
在曾经的一次面试经历中,被问到这样的问题:如何处理长度有几十位甚至上百位整型数字的加减法?
这是个很有意思的问题,当时我并没能给出令人满意的答案,但这个问题一直围绕在我的脑海,至今,我想把它实现一下。其实这个问题的难度已经被有意的缩减了,因为这里只说到加减法,如果要用到乘除法,那可能会更为麻烦一点,这是其一。其二,没有涉及小数位。那么下面我们就先来实现超长整型的加减法吧。
(注:在写本篇学习笔记时,本人学习和参考了网络文章,并做了借鉴,感谢各位前辈的分享。如果本文对您有所帮助,您可以随意分享,如果发现文中有误,也请指教,谢谢。本文用到的调试工具: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 1000

class 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: