溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

實現(xiàn)大數(shù)四則運(yùn)算

發(fā)布時間:2020-07-27 09:41:26 來源:網(wǎng)絡(luò) 閱讀:568 作者:威尼斯小艇 欄目:編程語言

        由于編程語言提供的基本數(shù)值數(shù)據(jù)類型表示的數(shù)值范圍有限,不能滿足較大規(guī)模的高精度數(shù)值計算,因此需要利用其他方法實現(xiàn)高精度數(shù)值的計算,于是產(chǎn)生了大數(shù)運(yùn)算。大數(shù)運(yùn)算主要有加、減、乘三種方法。那么大數(shù)到底如何進(jìn)行運(yùn)算呢,學(xué)習(xí)過數(shù)據(jù)結(jié)構(gòu)的都知道線性表,將大數(shù)拆分然后存儲在線性表中,不失為一個很好的辦法,下面通過字符串實現(xiàn)大數(shù)的構(gòu)造及四則運(yùn)算。

頭文件如下:

#ifndef BIG_DATA_H
#define BIG_DATA_H

#include<iostream>
using namespace std;
#include<assert.h>
#include<string>
 
#define UN_INTT 0xCCCCCCCCCCCCCCCC
#define MAX_INT64 0x7FFFFFFFFFFFFFFF
#define MIN_INT64 0x8000000000000000

typedef long long INT64;

//內(nèi)置類型
class BigData
{
public:
    BigData(INT64 value);
	BigData(const char* pData);//對大數(shù)進(jìn)行處理,優(yōu)化
	friend std::ostream& (operator<<(std::ostream& _cout, const BigData& bigdata));//輸出大數(shù)
public:
	//大數(shù)的四則運(yùn)算
	BigData operator+(const BigData& bigdata);//返回值不能傳引用
	BigData operator-(const BigData& bigdata);
	BigData operator*(const BigData& bigdata);
	BigData operator/(const BigData& bigdata);
	std::string Add(std::string left, std::string right);
	std::string Sub(std::string left, std::string right);
	std::string Mul(std::string left, std::string right);
	std::string Div(std::string left, std::string right);
private:
	bool IsINT64OverFlow()const;//判斷大數(shù)是否溢出
	void INT64ToString();//由于值在_value中,需將_value轉(zhuǎn)化成string類型
	char SubLoop(char* pLeft, int Lsize, const char* pRight, int Rsize);//循環(huán)相減
	bool IsLeftstrBig(const char* pLeft, int Lsize, const char* pRight, int Rsize);//判斷是否left大于right
private:
	std::string _strData;
	INT64 _value;
};

#endif

大數(shù)運(yùn)算的構(gòu)造函數(shù),下面對類似以下情況進(jìn)行處理:

"     ","+123456","00001234","12345xyz","123456789"

具體實現(xiàn)如下:

BigData::BigData(INT64 value)
      :_value(value)
{
	INT64ToString();//在構(gòu)造函數(shù)時將數(shù)字轉(zhuǎn)化成字符串
}

BigData::BigData(const char* pData)//對大數(shù)進(jìn)行處理,優(yōu)化
{
	//幾種情況:"     ","+123456","00001234","12345xyz","123456789";
	if (NULL == pData)
	{
		assert(false);
		return;
	}
	char* pStr = (char*)pData;
	char cSymbol = pData[0];//標(biāo)志符號位
	if ('+' == cSymbol || '-' == cSymbol)
	{
		pStr++;
	}
	else if (cSymbol >= '0' && cSymbol <= '9')
	{
		cSymbol = '+';
	}
	else return;
	//"00001234"
	while ('0' == *pStr)
	{
		pStr++;
	}
	//"12345xyz"
	_strData.resize(strlen(pData) + 1);//string中resize()函數(shù)改變本字符串的大小
	_strData[0] = cSymbol;//第一位存放符號位
	_value = 0;
	int icount = 1;
	while (*pStr >= '0' && *pStr <= '9')
	{
		_value = 10 * _value + *pStr - '0';
		_strData[icount++] = *pStr;
		pStr++;
	}
	_strData.resize(icount);//將本字符串的大小調(diào)到icount
	if ('-' == cSymbol)
	{
		_value = 0 - _value;//負(fù)值
	}
}

bool BigData::IsINT64OverFlow()const//判斷大數(shù)是否溢出
{//64位中數(shù)字范圍為:[-Ox8FFFFFFF FFFFFFFF,Ox7FFFFFFF FFFFFFFF]
	std::string temp = "+9223372036854775807";
	if ('-' == _strData[0])
	{
		temp = "-9223372036854775808";
	}
	//比較該大數(shù)與邊界的size,相等時進(jìn)行字符串直接比較
	if (_strData.size() < temp.size() || _strData.size() == temp.size() && _strData <= temp)
		return true;
	else
		return false;
}
void BigData::INT64ToString()//將_value轉(zhuǎn)化成string類型
{//append()在字符串的末尾添加num個字符ch-----basic_string &(append(size_t num,char ch))
	char cSymbol = '+';
	if (_value < 0)
	{
		cSymbol = '-';
	}
	//12345->"54321"
	INT64 temp = _value;
	_strData.append(1, cSymbol);
	if (cSymbol == '-')//負(fù)數(shù)轉(zhuǎn)變成正數(shù)再模除
	{
		temp = 0 - temp;
	}
	while (temp)
	{
		_strData.append(1, temp % 10 + '0');
		temp /= 10;
	}
	//"54321"->"12345"
	char* pLeft = (char*)_strData.c_str() + 1;
	char* pRight = pLeft + _strData.size() - 2;//包含符號位,故減2
	while (pLeft < pRight)
	{
		char tmp = *pLeft;
		*pLeft = *pRight;
		*pRight = tmp;
		pLeft++;
		pRight--;
	}
}

自主實現(xiàn)大數(shù)的輸出,代碼如下:

td::ostream& (operator<<(std::ostream& _cout, const BigData& bigdata))//輸出大數(shù)
{//判斷是否溢出,'+'不需要輸出
	if (bigdata.IsINT64OverFlow())//沒有溢出
	{
		_cout << bigdata._value << std::endl;
	}
	else
	{//c_str()函數(shù)返回一個指向正規(guī)C字符串的指針, 內(nèi)容與本字符串相同
		char* pData = (char*)bigdata._strData.c_str();
		if ('+' == pData[0])
			pData++;
		_cout << pData << std::endl;
	}
	return _cout;
}

大數(shù)的加法運(yùn)算:

BigData BigData::operator+(const BigData& bigdata)
{
	//兩個數(shù)都沒溢出,結(jié)果也沒溢出,直接進(jìn)行相加
	if (IsINT64OverFlow() && bigdata.IsINT64OverFlow())
	{
		//兩個數(shù)一正一負(fù)
		if (_strData[0] != bigdata._strData[0])
		{
			return _value + bigdata._value;
		}
		else
		{//兩個數(shù)同號,沒溢出的情況:如果邊界是10,則10-3=7>=6 8,-10-(-3)=-7=<-6 -8
			if ((_value >= 0 && (MAX_INT64 - _value >= bigdata._value)) ||
				(_value < 0 && (MIN_INT64 - _value <= bigdata._value)))
			{
				return _value + bigdata._value;
			}
			else
			{
				return BigData(Add(_strData, bigdata._strData).c_str());
			}
		}
	}
	//兩個數(shù)至少一個溢出,結(jié)果溢出
	//同號
	if (_strData[0] == bigdata._strData[0])
	{
		return BigData(Add(_strData, bigdata._strData).c_str());//c._str(),size()
	}
	//異號
	else
	{//兩個數(shù)異號a,b;b為正數(shù)需要換負(fù)號,b為負(fù)數(shù)需要換正號
		string _StrData = bigdata._strData;//注意在此處定義字符串,不是BigData
		if (_StrData[0] == '+')
		{
			_StrData[0] = '-';
		}
		else
		{
			_StrData[0] = '+';
		}
		return BigData(Sub(_strData, _StrData).c_str());
	}
	return BigData(INT64(0));
}
std::string BigData::Add(std::string left, std::string right)
{
	int iLsize = left.size();
	int iRsize = right.size();
	if (iLsize < iRsize)//只需要左邊為長度長的即可
	{
		std::swap(left, right);
		std::swap(iLsize, iRsize);
	}
	std::string sRet;
	sRet.resize(iLsize + 1);//相加不會超過較大數(shù)的size+1
	sRet[0] = left[0];
	char Step = 0;//進(jìn)位
	//通過模除得到每位的數(shù)字及進(jìn)位Step
	for (int iIdx = 1; iIdx < iLsize;iIdx++)
	{
		int cRet = left[iLsize - iIdx] + Step - '0';
		if (iIdx < iRsize)
		{
			cRet += right[iRsize - iIdx] - '0';//cRet轉(zhuǎn)為數(shù)字,-‘0’
		}
		sRet[iLsize - iIdx + 1] = cRet % 10 + '0';//sRet比iLsize多一位,存放字符,故+‘0’
		Step = cRet / 10;
	}
	sRet[1] = Step + '0';
	return sRet;
}

大數(shù)的減法運(yùn)算:

BigData BigData::operator-(const BigData& bigdata)
{
	//兩個數(shù)都沒溢出,結(jié)果也沒溢出,直接進(jìn)行相加
	if (IsINT64OverFlow() && bigdata.IsINT64OverFlow())
	{
		if (_strData[0] == bigdata._strData[0])
		{
			return _value + bigdata._value;
		}
		else
		{
			//兩個數(shù)異號,相減沒溢出
			//-10 + 3 = -7 <= -6(減式:3-(-6));10+(-3)= 7 >= 6(減式:-3-(6))
			if (_value >= 0 && (MIN_INT64 + _value <= bigdata._value) ||
				_value < 0 && (MAX_INT64 + _value >= bigdata._value))
			{
				return _value + bigdata._value;
			}
			else
			{//不用使bigdata._strData[0]設(shè)為'-',Add符號隨左邊數(shù)字即被減數(shù)(-9999-1 = -10000)
				BigData(Add(_strData, bigdata._strData).c_str());
			}
		}
	}
	//兩個數(shù)至少一個溢出,
	//同號調(diào)用減法
	if (_strData[0] == bigdata._strData[0])
	{
		return BigData(Sub(_strData, bigdata._strData).c_str());
	}
	else
	{
		return BigData(Add(_strData, bigdata._strData).c_str());
	}
	return BigData(INT64(0));
}
std::string BigData::Sub(std::string left, std::string right)
{
	int iLsize = left.size();
	int iRsize = right.size();
	char cSymbol = left[0];//保存所得差的符號位
	if (iLsize < iRsize || (iLsize==iRsize && left < right))//左邊小于右邊都進(jìn)行交換
	{//-12-(-21)=9,21-34=-13發(fā)現(xiàn)兩數(shù)的差與減數(shù)的相反
		std::swap(left,right);
		std::swap(iLsize, iRsize);
		if (cSymbol == '-')
		{
			cSymbol = '+';
		}
		else
		{
			cSymbol = '-';
		}
	}
	std::string sRet;
	sRet.resize(iLsize);
	sRet[0] = cSymbol;//保存符號位
	for (int iIdx = 1; iIdx < iLsize; iIdx++)//結(jié)束標(biāo)志為iLsize-1,不是iLsize
	{
		//從低到高,取left每一位;
		char cRet = left[iLsize - iIdx] - '0';
		//在right范圍內(nèi)從低到高,取right每一位,然后相減;
		if (iIdx < iRsize)
		{
			cRet -= right[iRsize - iIdx] - '0';
		}
		//判斷是否借位
		if (cRet < 0)
		{
			left[iLsize - iIdx - 1] -= 1;
			cRet += 10;
		}
		sRet[iLsize - iIdx] = cRet + '0';
	}
	return sRet;
}

大數(shù)的乘法運(yùn)算:

BigData BigData::operator*(const BigData& bigdata)
{
	if (IsINT64OverFlow() && bigdata.IsINT64OverFlow())
	{
		if (_strData[0] == bigdata._strData[0])
		{
		    //例如:邊界是10,10 / 2 = 5 > 4; 10 / -2 = -5 < -4;
			if (_value > 0 && MAX_INT64 / _value >= bigdata._value ||
				_value < 0 && MAX_INT64 / _value <= bigdata._value)
			{
				return _value*bigdata._value;
			}
		}
		else
		{
			//例如:邊界是-10,-10 / 2 = -5 < -4; -10 / -2 = 5 > 4;
			if (_value>0 && MIN_INT64 / _value <= bigdata._value ||
				_value < 0 && MIN_INT64 / _value >= bigdata._value)
			{
				return _value*bigdata._value;
			}
		}
	}
	//兩數(shù)至少有一個溢出
	if (_value != 0 && bigdata._value != 0)
	{
		return BigData(Mul(_strData, bigdata._strData).c_str());
	}
	return BigData(INT64(0));
}
std::string BigData::Mul(std::string left, std::string right)
{
	int iLsize = left.size();
	int iRsize = right.size();
	char cSymbol = '+';//確認(rèn)符號位
	if (left[0] != right[0])
	{
		cSymbol = '-';
	}
	if (iLsize > iRsize)//使較小的數(shù)為left,提高效率。eg:99*12345678
	{
		swap(left, right);
		swap(iLsize, iRsize);
	}
	std::string sRet;
	//兩個數(shù)相乘最大位數(shù)為兩個數(shù)位數(shù)的和,left和right中有符號位故減1
	sRet.assign(iLsize + iRsize - 1, '0');//assign()為字符串賦新值'0'
	sRet[0] = cSymbol;
	int iDataLen = iLsize + iRsize - 1;
	int ioffset = 0;//移位
	//先取左邊一位和右邊相乘;再考慮移位可得到左邊每位與右邊相乘的結(jié)果
	for (int iLdx = 1; iLdx < iLsize; iLdx++)
	{
		char cLeft = left[iLsize - iLdx] - '0';
		if (cLeft == 0)//如果left中含有0,偏移量加1
		{
			ioffset++;
			continue;
		}
		char Step = 0;
		//99*999=89910+8991;
		for (int iRdx = 1; iRdx < iRsize; iRdx++)
		{
			char cRet = cLeft * (right[iRsize - iRdx] - '0') + Step;
			cRet += sRet[iDataLen - iRdx - ioffset] - '0';//cRet存放當(dāng)前位最終乘加的結(jié)果
			sRet[iDataLen - iRdx - ioffset] = cRet % 10 + '0';//存放字符+'0'
			Step = cRet / 10;
		}
		sRet[iDataLen - iRsize - ioffset] += Step;
		ioffset++;
	}
	return sRet;
}

大數(shù)的除法運(yùn)算:

BigData BigData::operator/(const BigData& bigdata)
{
	//1、除數(shù)為0
	if (bigdata._strData[1] == '0')
	{
		assert(false);
	}
	//2、兩個數(shù)沒溢出
	if (IsINT64OverFlow() && bigdata.IsINT64OverFlow())
	{
		return _value / bigdata._value;
	}
	//3、除數(shù)為1或-1
	if (bigdata._value == 1 || bigdata._value == -1)
	{
		return _value;
	}
	//4、除數(shù)和被除數(shù)相等
	//if (strcmp(_strData.data() + 1, bigdata._strData.data() + 1) == 0)
	//data()返回內(nèi)容的字符數(shù)組形式
	if (strcmp(_strData.c_str() + 1, bigdata._strData.c_str() + 1) == 0)
	{
		if (_strData[0] != bigdata._strData[0])
		{
			return BigData(INT64(-1));
		}
		return BigData(INT64(1));
	}
	if (_strData.size() < bigdata._strData.size() ||
		_strData.size() == bigdata._strData.size() &&
		strcmp(_strData.c_str() + 1, bigdata._strData.c_str() + 1) < 0)
	{
		return BigData(INT64(0));
	}
	return BigData(Div(_strData, bigdata._strData).c_str());
}
std::string BigData::Div(std::string left, std::string right)
{//此處用append()對字符串依次賦值
	std::string sRet;
	sRet.append(1, '+');
	if (left[0] != right[0])
	{
		sRet[0] = '-';
	}
	char* pLeft = (char*)left.c_str() + 1;
	char* pRight = (char*)right.c_str() + 1;
	int DataLen = right.size() - 1;//標(biāo)記相除的除數(shù)位數(shù)
	int Lsize = left.size() - 1;
	int Rsize = right.size() - 1;
	//eg:222222/33首先取到22和33比較大小,如果大就直接相除,否則DataLen++;
	for (int iIdx = 0; iIdx < Lsize;)
	{
		if (!(IsLeftstrBig(pLeft, DataLen, pRight, Rsize)))//如果取到的數(shù)小于除數(shù)時,結(jié)果商0,向后再取一位
		{
			sRet.append(1, '0');
			DataLen++;
		}
		else
		{
			sRet.append(1, SubLoop(pLeft, DataLen, pRight, Rsize));//循環(huán)相減得到該位的商
			//判斷pLeft中進(jìn)行循環(huán)相減后依次去掉0,
			while (*pLeft == '0' && DataLen > 0)
			{
				pLeft++;
				DataLen--;
				iIdx++;
			}
			DataLen++;
		}
		if (DataLen > Rsize + 1)//pLeft比pRight大一位結(jié)果為0,則pLeft中含有0
		{
			pLeft++;
			DataLen--;
			iIdx++;
		}
		if (DataLen + iIdx > Lsize)//判斷是否除法結(jié)束
			break;
	}
	return sRet;
} 
char BigData::SubLoop(char* pLeft, int Lsize, const char* pRight, int Rsize)
{
	assert(pLeft && pRight);
	char cRet = 0;
	while (IsLeftstrBig(pLeft, Lsize, pRight, Rsize))//直到被減數(shù)小于減數(shù)停止運(yùn)算
	{
		for (int iIdx = 0; iIdx < Rsize; iIdx++)//進(jìn)行減運(yùn)算
		{
			char ret = pLeft[Lsize - iIdx - 1] - '0';
			ret -= pRight[Rsize - iIdx - 1] - '0';
			if (ret < 0)
			{
				pLeft[Lsize - iIdx - 2] -= 1;
				ret += 10;
			}
			pLeft[Lsize - iIdx - 1] = ret + '0';
		}
		while (*pLeft == '0'&&Lsize>0)
		{
			pLeft++;
			Lsize--;
		}
		cRet++;
	}
	return cRet + '0';
}
bool BigData::IsLeftstrBig(const char* pLeft, int Lsize, const char* pRight, int Rsize)//判斷是否left大于right
{
	assert(pLeft && pRight);
	char* pleft = (char*)pLeft;
	char* pright = (char*)pRight;
	if (Lsize > Rsize && *pleft > '0')//eg:112和33
	{
		return true;
	}
	else if (Lsize == Rsize)//eg:57和33
	{
		while (pright)
		{
			if (*pleft > *pright)
			{
				return true;
			}
			else if (*pleft == *pright)
			{
				pleft++;
				pright++;
			}
			else
				return false;
		}
	}
	return false;
}
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI