C++实现大数运算

发布时间:2020-10-13 20:20:14 作者:zgw285763054
来源:网络 阅读:3006

项目背景:

    大数运算,顾名思义,就是很大的数值的数进行一系列的运算。

    我们知道,在数学中,数值的大小是没有上限的,但是在计算机中,由于字长的限制,计算机所能表示的范围是有限的,当我们对比较小的数进行运算时,如:1234+5678,这样的数值并没有超出计算机的表示范围,所以可以运算。但是当我们在实际的应用中进行大量的数据处理时,会发现参与运算的数往往超过计算机的基本数据类型的表示范围,比如说,在天文学上,如果一个星球距离我们为100万光年,那么我们将其化简为公里,或者是米的时候,我们会发现这是一个很大的数。这样计算机将无法对其进行直接计算。

    可能我们认为实际应用中的大数也不过就是几百位而已,实际上,在某些领域里,甚至可能出现几百万位的数据进行运算,这是我们很难想象的。如果没有计算机,那么计算效率可想而知。

    由于编程语言提供的基本数值数据类型表示的数值范围有限,不能满足较大规模的高精度数值计算,因此需要利用其他方法实现高精度数值的计算,于是产生了大数运算。本项目实现了大数运算主要的加、减、乘除四种方法。


BigData.h

#ifndef BIG_DATA_H
#define BIG_DATA_H

#include <string>

#define MAX_INT64 (INT64)0x7FFFFFFFFFFFFFFF
#define MIN_INT64 (INT64)0x8000000000000000 //如果不强转,系统会定义为unsigned INT64类型

#define UN_INIT 0xCCCCCCCCCCCCCCCC
typedef long long INT64;

class BigData
{
public:
	BigData(INT64 value);
	BigData(const char* pData);

public:
	BigData operator+(const BigData& bigdata);
	BigData operator-(const BigData& bigdata);
	BigData operator*(const BigData& bigdata);
	BigData operator/(const BigData& bigdata);

protected:
	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);	

protected:
	bool IsINT64Overflow() const;
	friend std::ostream& operator<<(std::ostream& _cout, const BigData& bigdata);
	void INT64ToString();
	bool IsLeftStrBig(const char* pLeft, int LSize, const char* pRight, int RSize);
	char SubLoop(char* pLeft, int LSize, char* pRight, int RSize);

protected:
	INT64 _value;
	std::string _strData;
};

#endif

BigData.cpp

#include "BigData.h"
#include <assert.h>

BigData::BigData(INT64 value)
	:_value(value)
{
	INT64ToString(); //将_value中的值保存在_strData中
}

BigData::BigData(const char* pData)
{
	//要处理的输入:"12345678"   "234567qwe"    "+"    "    "     "0000123456"

	if (NULL == pData)
	{
		assert(false);
		return;
	}

	//处理符号位
	char* pStr = (char*)pData; 
	char cSymbol = pData[0];
	if ('+' == cSymbol || '-' == cSymbol)
		pStr++;
	else if (cSymbol >= '0' && cSymbol <= '9')
		cSymbol = '+';
	else
		return;

	//"0000123456"
	int iDataLen = strlen(pData);
	if (iDataLen > 1)
	{
		while ('0' == *pStr)
			pStr++;
	}

	_strData.resize(strlen(pData)+1); //为_strData分配空间
	_strData[0] = cSymbol; //第0位保存符号

	//"123456qwe"
	_value = 0;
	int iCount = 1;
	while (*pStr >= '0' && *pStr <= '9')
	{
		_value = _value*10 + (*pStr - '0');
		_strData[iCount++] = *pStr;
		pStr++;
	}

	_strData.resize(iCount);

	if (cSymbol == '-')
		_value = 0 - _value;
}

bool BigData::IsINT64Overflow() const
{
	std::string temp("+9223372036854775807");
	
	if (_strData[0] == '-')
		temp = "-9223372036854775808";

	if (_strData.size() < temp.size())
		return false;
	else if (_strData.size() == temp.size() && _strData <= temp)
		return false;

	return true;
}

void BigData::INT64ToString()
{
	//处理符号位
	char cSymbol = '+';
	if (_value < 0)
		cSymbol = '-';

	INT64 temp = _value;
	while (temp)
	{
		if (cSymbol == '+')
			_strData.append(1, temp%10 + '0');
		else
			_strData.append(1, -(temp%10) + '0');

		temp /= 10;
	}
	_strData.append(1, cSymbol);
	
	std::reverse(_strData.begin(), _strData.end());
}

bool BigData::IsLeftStrBig(const char* pLeft, int LSize, const char* pRight, int RSize)
{
	assert(pLeft != NULL && pRight != NULL);

	if ((LSize > RSize) || 
		(LSize == RSize && strcmp(pLeft, pRight) >= 0))
		return true;
	else
		return false;
}

char BigData::SubLoop(char* pLeft, int LSize, char* pRight, int RSize)
{
	assert(pLeft != NULL && pRight != NULL);

	char cRet = '0';

	while (true)
	{
		if (!IsLeftStrBig(pLeft, LSize, pRight, RSize))
			break;
		
		int iLIdx = LSize - 1;
		int iRIdx = RSize - 1;
		while (iLIdx >= 0 && iRIdx >= 0)
		{
			char ret = pLeft[iLIdx] - '0';
			ret -= pRight[iRIdx] - '0';
			
			if (ret < 0)
			{
				pLeft[iLIdx - 1] -= 1;
				ret += 10;
			}
			
			pLeft[iLIdx] = ret + '0';
			iLIdx--;
			iRIdx--;
		}

		while (*pLeft == '0' && LSize > 0)
		{
			pLeft++;
			LSize--;
		}

		cRet++;
	}

	return cRet;
}

std::ostream& operator<<(std::ostream& _cout, const BigData& bigdata)
{
	if (!bigdata.IsINT64Overflow()) //_value没有溢出
	{
		_cout<<bigdata._value;
	}
	else //_value溢出
	{
		char* pData = (char*)bigdata._strData.c_str();
		if ('+' == pData[0])
			pData++;

		_cout<<pData;
	}

	return _cout;
}

BigData BigData::operator+(const BigData& bigdata)
{
	if (!IsINT64Overflow() && !bigdata.IsINT64Overflow()) //两个数都没有溢出
	{
		if (_strData[0] != bigdata._strData[0]) //两个数异号
		{
			return BigData(_value + bigdata._value);
		}
		else //两个数同号
		{
			if ((_value >= 0 && bigdata._value <= MAX_INT64 - _value) || 
				(_value < 0 && bigdata._value >= MIN_INT64 - _value)) //相加后的和没有溢出
				return BigData(_value + bigdata._value);
		}
	}

	//两个数至少有一个溢出
	//相加后的和溢出
	if (_strData[0] == bigdata._strData[0]) //同号相加,调用加法
		return BigData(Add(_strData, bigdata._strData).c_str());
	else //异号相加,调用减法
		return BigData(Sub(_strData, bigdata._strData).c_str());
}

BigData BigData::operator-(const BigData& bigdata)
{
	if (!IsINT64Overflow() && !bigdata.IsINT64Overflow()) //两个数都没有溢出
	{
		if (_strData[0] == bigdata._strData[0])//同号
		{
			return BigData(_value - bigdata._value);
		}
		else //异号
		{
			if ((_value >= 0 && MAX_INT64 + bigdata._value >= _value) || 
				(_value < 0 && MIN_INT64 + bigdata._value <= _value)) //结果没有溢出
				return BigData(_value - bigdata._value);
		}
	}

	//至少有一个数溢出或结果溢出
	if (_strData[0] != bigdata._strData[0]) //异号相减,调用加法
		return BigData(Add(_strData, bigdata._strData).c_str());
	else //同号相减,调用减法
		return BigData(Sub(_strData, bigdata._strData).c_str());
}

BigData BigData::operator*(const BigData& bigdata)
{
	if (!IsINT64Overflow() && !bigdata.IsINT64Overflow()) //都没有溢出
	{
		if (_value == 0 || bigdata._value == 0)
		{
			return BigData((INT64)0);
		}

		if (_strData[0] == bigdata._strData[0]) //同号,积为正
		{
			if ((_value > 0 && MAX_INT64/_value >= bigdata._value) || 
				(_value < 0 && MAX_INT64/_value <= bigdata._value)) //积没有溢出
			{
				return BigData(_value * bigdata._value);
			}
		}
		else //异号,积为负
		{
			if ((_value > 0 && MIN_INT64/_value <= bigdata._value) || 
				(_value < 0 && MIN_INT64/_value >= bigdata._value)) //积没有溢出
			{
				return BigData(_value * bigdata._value);
			}
		}
	}

	return BigData(Mul(_strData, bigdata._strData).c_str());
}

BigData BigData::operator/(const BigData& bigdata)
{
	//除数不能为0
	if ('0' == bigdata._strData[1])
		assert(false);

	//	都没溢出


	if (!IsINT64Overflow() && !bigdata.IsINT64Overflow())
		return BigData(_value / bigdata._value);

	//	至少有一个溢出


	//除数 == ±1
	if (bigdata._strData == "+1" && bigdata._strData == "-1") 
	{
		std::string strRet = _strData;

		if (_strData[0] != bigdata._strData[0])
			strRet[0] = '-';

		return BigData(strRet.c_str());
	}

	//左 < 右
	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));

	//左 == 右
	if (strcmp(_strData.c_str()+1, bigdata._strData.c_str()+1) == 0) //左==右
	{
		std::string strRet = "+1";
		if (_strData[0] != bigdata._strData[0])
			strRet[0] = '-';
		else
			return BigData(strRet.c_str());
	}

	//左 > 右
	return BigData(Div(_strData, bigdata._strData).c_str());
}

std::string BigData::Add(std::string left, std::string right)
{
	//让left保存位数大的数
	int iLeftSize = left.size();
	int iRightSize = right.size();
	if (iLeftSize < iRightSize)
	{
		std::swap(left, right);
		std::swap(iLeftSize, iRightSize);
	}

	//相加
	std::string strRet;
	strRet.resize(iLeftSize + 1);
	strRet[0] = left[0]; //符号位
	char Step = 0; //进位
	for (int iIdx = 1; iIdx < iLeftSize; ++iIdx)
	{
		char cRet = left[iLeftSize - iIdx] - '0' + Step;

		if (iIdx < iRightSize)
			cRet += right[iRightSize - iIdx] - '0';

		strRet[iLeftSize - iIdx + 1] = cRet % 10 + '0';
		Step = cRet / 10;
	}

	strRet[1] = Step + '0';
	
	return strRet;
}

std::string BigData::Sub(std::string left, std::string right)
{
	int iLeftSize = left.size();
	int iRightSize = right.size();
	char cSymbol = left[0]; //差的符号位

	if ((iLeftSize < iRightSize) || 
		(iLeftSize == iRightSize && left < right))
	{
		std::swap(left, right);
		std::swap(iLeftSize, iRightSize);

		if (cSymbol == '+')
			cSymbol = '-';
		else
			cSymbol = '+';
	}

	//相减
	std::string strRet; //保存差
	strRet.resize(left.size()); //先设置和左操作数一样大的空间
	strRet[0] = cSymbol; //符号位

	for (int Idx = 1; Idx < iLeftSize; ++Idx)
	{
		char cRet = left[iLeftSize - Idx] - '0';
		
		if (Idx < iRightSize)
			cRet -= (right[iRightSize - Idx] - '0');

		if (cRet < 0) //有借位
		{
			left[iLeftSize - Idx - 1] -= 1;
			cRet += 10;
		}

		strRet[iLeftSize - Idx] = cRet + '0';
	}

	return strRet;
}

std::string BigData::Mul(std::string left, std::string right)
{
	//确定符号位
	char cSymbol = '+';
	if (left[0] != right[0])
		cSymbol = '-';

	//使左操作数位数小于右操作数位数
	int iLeftSize = left.size();
	int iRightSize = right.size();
	if (iLeftSize > iRightSize)
	{
		std::swap(left, right);
		std::swap(iLeftSize, iRightSize);
	}

	std::string strRet;
	//strRet.resize(iLeftSize+iRightSize-1);
	strRet.assign(iLeftSize+iRightSize-1, '0');
	strRet[0] = cSymbol;
	int iRetLen = strRet.size();
	int iOffset = 0; //偏移

	for (int iLeftIndex = 1; iLeftIndex < iLeftSize; ++iLeftIndex)
	{
		char cLeft = left[iLeftSize - iLeftIndex] - '0';
		
		if (cLeft == 0) //当左操作数中有0时,直接左移,不计算
		{
			++iOffset;
			continue;
		}

		char cStep = 0;
		for (int iRightIndex = 1; iRightIndex < iRightSize; ++iRightIndex)
		{
			char cRet = cLeft*(right[iRightSize - iRightIndex] - '0') + cStep;
			cRet += strRet[iRetLen - iRightIndex - iOffset] - '0';
			strRet[iRetLen - iRightIndex - iOffset] = (cRet%10) + '0';
			cStep = cRet/10;
		}
		strRet[iRetLen-iRightSize -iOffset] += cStep;
		++iOffset;
	}

	return strRet;
}

std::string BigData::Div(std::string left, std::string right)
{
	std::string strRet;
	
	//确定符号位
	strRet.append(1, '+');
	if (left[0] != right[0])
		strRet[0] = '-';

	char* pLeft = (char*)(left.c_str()+1);
	char* pRight = (char*)(right.c_str()+1);
	int iLSize = left.size()-1; //被除数长度
	int iDataLen = right.size()-1; //取iDataLen位被除数

	int iIdx = 0; //iIdx和pLeft同步走,用来判断是否走到被除数结尾
	while (iIdx < iLSize)
	{
		if (*pLeft == '0')
		{
			strRet.append(1, '0');
			pLeft++;
			iIdx++;
			continue;
		}

		if (!IsLeftStrBig(pLeft, iDataLen, pRight, right.size()-1)) //取的iDataLen位被除数 < 除数
		{
			strRet.append(1, '0');
			iDataLen++;
			if (iDataLen + iIdx > iLSize)
				break;
		}
		else
		{
			//循环相减
			strRet.append(1, SubLoop(pLeft, iDataLen, pRight, right.size()-1)); 
			while (*pLeft == '0')
			{
				pLeft++;
				iIdx++;
				iDataLen--;
			}

			iDataLen++;
		}
	}

	return strRet;
}

BigDataTest.cpp

#include <iostream>
using namespace std;
#include "BigData.h"

void AddTest()
{
	//BigData b1("12345678");
	//BigData b2("12345qwe");
	//BigData b3("+");
	//BigData b4("00001234");
	//BigData b5("   ");
	//BigData b6("99999999999999999999999999999999999999999999999999999999");
	//BigData b7 ("-123213");

	//cout<<b1<<endl;
	//cout<<b2<<endl;
	//cout<<b3<<endl;
	//cout<<b4<<endl;
	//cout<<b5<<endl;
	//cout<<b6<<endl;
	//cout<<b7<<endl;

	//BigData left1(1234);
	//BigData right1(4321);
	//cout<<left1+right1<<endl;

	//BigData left2(9223372036854775807);
	//BigData right2(3);
	//cout<<left2+right2<<endl;

	//BigData left3(-9223372036854775808);
	//BigData right3(-3);
	//cout<<left3+right3<<endl;

	//BigData left3("99999999999999999999999999999999999");
	//BigData right3("11111111111111111111111111111111111");
	//cout<<left3+right3<<endl;
}

void SubTest()
{
	//BigData left("1111111111111111111111111111111100");
	//BigData right("99");
	//cout<<left-right<<endl;

	//BigData left1("-2222222222222222222222222222222222222222");
	//BigData right1("22222");
	//cout<<left1+right1<<endl;
}

void MulTest()
{
	//BigData left("77777777777777777777777777777777777777777777777777777777777");
	//BigData right("66");
	//cout<<left*right<<endl;

	//BigData left1("99");
	//BigData right1("9999999999999999999999999999999999999999999");
	//cout<<left1*right1<<endl;


	//BigData left2("11111111111111111111111111111111111111111111111111111111111111111");
	//BigData right2("-99");
	//cout<<left2*right2<<endl;

	//BigData left3("-8");
	//BigData right3("66");
	//cout<<left3*right3<<endl;

	//BigData left4("10001");
	//BigData right4("666666666666666666666666666666666666");
	//cout<<left4*right4<<endl;
}

void DivTest()
{
	//BigData left("222222222222222222222222222222222222222222222222");
	//BigData right("33");
	//cout<<left/right<<endl;

	//BigData left1("-222222222222222222222222222222222222222222222222");
	//BigData right1("33");
	//cout<<left1/right1<<endl;

	//BigData left3("33333333333333333333333333333333333333333333333333333");
	//BigData right3("0");
	//cout<<left3/right3<<endl;

	//BigData left4("111");
	//BigData right4("222222222222222222222222222222222222222222222222");
	//cout<<left4/right4<<endl;

	//BigData left5("11111111111111111111111111111111111111111111111111");
	//BigData right5("-11111111111111111111111111111111111111111111111111");
	//cout<<left5/right5<<endl;
}

void PrintMenu()
{
	cout<<"------------------------------------------------------------------------"<<endl;
	cout<<"|                            大 数 计 算 器                            |"<<endl;
	cout<<"------------------------------------------------------------------------"<<endl;
	cout<<"请输入:";
}

void Demo()
{
	while (true)
	{
		PrintMenu();
		string input;
		cin>>input;

		if (input == "quit")
		{
			break;
		}

		if (input.find('+') != -1)
		{
			string left = input.substr(0, input.find('+'));
			string right = input.substr(input.find('+')+1, input.length()-1);
			BigData bigdata1(left.c_str());
			BigData bigdata2(right.c_str());
			cout<<"结果:  "<<bigdata1+bigdata2<<endl;
			continue;
		}

		if (input.find('-') != -1)
		{
			string left = input.substr(0, input.find('-'));
			string right = input.substr(input.find('-')+1, input.length()-1);
			BigData bigdata1(left.c_str());
			BigData bigdata2(right.c_str());
			cout<<"结果:  "<<bigdata1-bigdata2<<endl;
			continue;
		}

		if (input.find('*') != -1)
		{
			string left = input.substr(0, input.find('*'));
			string right = input.substr(input.find('*')+1, input.length()-1);
			BigData bigdata1(left.c_str());
			BigData bigdata2(right.c_str());
			cout<<"结果:  "<<bigdata1*bigdata2<<endl;
			continue;
		}

		if (input.find('/') != -1)
		{
			string left = input.substr(0, input.find('/'));
			string right = input.substr(input.find('/')+1, input.length()-1);
			BigData bigdata1(left.c_str());
			BigData bigdata2(right.c_str());
			cout<<"结果:  "<<bigdata1/bigdata2<<endl;
			continue;
		}

	}
}

int main()
{
	//AddTest();
	//SubTest();
	//MulTest();
	//DivTest();
	Demo();

	return 0;
}


下面是一个简单的测试:

C++实现大数运算

推荐阅读:
  1. 大数运算之字符串模拟
  2. 小项目--大数运算

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

c++ 大数运算 c+

上一篇:redis 实现登陆次数限制的思路详解

下一篇:Android使用动画动态添加商品进购物车

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》