C++string类实现大数的加减

文章目录

      • 前期准备
      • 解题思路
      • 大数加减的框架
      • 实现代码

使用C++如何实现大数的加减(或乘除) ? 在开始之前 , 让我们来解释一下什么是大数 , 所谓的大数 , 就是那些超过内置类型所规定的长度的数 .比如说 , unsigned int型的变量范围是 : 0~4294967295 (到十亿位).
那么问题来了 , 如果想要计算百亿 ,千亿 ,甚至更高的数(超过内置类型所能表示的最大长度)怎么办 , 还能够依靠int型吗 .显然这个时候用给定好的类型是不能够完成的 , 那么我们就得思考怎么样来实现这样的大数运算 , 在C语言中 , 我们可以使用字符串来进行运算 , 那当然在C++中也是可以的.
但C++为我们提供了一个string类 , 用来更加方便的实现对字符串的操作 . 所以想要实现大数的运算 , string类是你最佳的选择 ;为什么这么说呢 ,因为 string类也属于容器的一种 , 它除了支持字符串的各种运算符的重载函数 , 还能够支持一些容器的操作.

前期准备

再开始说明算法实现之前 , 我们还需要一些前期的知识储备

  • string类支持[ ]运算符的重载;
  • string类型支持length() 得到字符串长度
  • string类支持push_back() 和 insert();
  • string类支持swap() 交换两个字符串;
  • 在泛型算法中有reverse()函数 , 传入两个迭代器 , 使之逆置迭代器中的数据;
  • #include< sstream >
    stringstream temp; //名字自己起
    可以实现string 与 数字 之间的转换 , 而且它还有一个优点 , 如果字符串表示一个负数(如 : “-250”) , 在转换为数字时 , 能够保留负号.
    注 : 其实在大数运算的时候 , 直接使用 字符+(或-) '0’即可 , 因为编者是头一回见 , 所以在代码中使用stringstream来达到数字与string的转换.

如何把数字转换为string类型?

	stringstream temp;
	int a=123456; 
	string s;
	temp<<a;
	temp>>s;
	cout<<"s : "<<s<<endl;

运行结果 :
在这里插入图片描述
如何把string类型转换为数字?

	stringstream temp;	
	int a; 
	string s("-250");
	temp<<s;
	temp>>a;
	cout<<"a :"<<a+1<<endl; //-249

运行结果 :
在这里插入图片描述

解题思路

再开始写代码之前 , 先要进行构思.
那么编者就来说说自己的思路 :

  • 在加法中 , 根据计算习惯 , 将较大的数放在上面(使用swap()) .这里的较大指的是字符串的长度 , 长的一定大.如果两个字符串长度相同 , 则无需改变 ;
  • 设置一个标志位 (flag), 用来考虑进位 ;
  • 如果对位相加 >= 10(字符 => 数字) , 则进位(flag = true) , 再将相加结果(%10)存入记录结果的string中; 如果 < 10 ,则直接写入结果 ; 因为对位相加 , 最大也就 9 +9= 18 ,不会超过20 ;
  • 在对位相加之前要判断之前的相加是否存在进位 , 如果有进位 , 则相加结果 +1 ;
  • 还要考虑 , 当较短的字符串遍历完毕时 , 较长的那个是否也遍历完毕 , 如果便利完毕(a , b 等长) , 是否存在进位 ;如果还没有遍历完毕(a , b 不等长) , 也要考虑进位 ;
  • 减法与之基本相似 , 大数在上 , 小数在下 ;唯一不同的是 , 加入了一个标志位(mark) , 用来记录计算结果的正负 ; 如果在开始时 , 两个数发生交换 , 则说明 , 相减的结果为负数(因为把较大的那个放在上边) ;

大数加减的框架

编者将框架放在这里 , 如有不同思路 , 可以互相交流

class BigInt
{
public:
	BigInt(string str) :strDigit(str){}
private:
	string strDigit;   // 使用字符串存储大整数
};
// 打印函数
ostream& operator<<(ostream &out, const BigInt &src);
// 大数加法
BigInt operator+(const BigInt &lhs, const BigInt &rhs);
// 大数减法
BigInt operator-(const BigInt &lhs, const BigInt &rhs);
int main()
{
	BigInt int1("28937697857832167849697653231243");
	BigInt int2("9785645649886874535428765");
	cout << int1 + int2 << endl;	
	//28937707643477817736572188660008
	cout << int1 - int2 << endl;
	//28937688072186517962823117802478
	return 0;
}

实现代码

#include 
#include   // 使用vector容器
#include   // 使用泛型算法
#include 
#include 
#include
using namespace std;

class BigInt
{
public:
	BigInt(string str) :strDigit(str){}
private:
	string strDigit;   // 使用字符串存储大整数
	//友元函数声明
	friend BigInt operator+(const BigInt &lhs, const BigInt &rhs);
	friend ostream& operator<<(ostream &out, const BigInt &src);
	friend BigInt operator-(const BigInt &lhs, const BigInt &rhs);
};
//打印函数
ostream& operator<<(ostream &out, const BigInt &src)
{
	out<<src.strDigit;
	return out;
}

// 大数加法
BigInt operator+(const BigInt &lhs, const BigInt &rhs)
{
	bool flag = false;      //进位
	stringstream tmpL;  // 数字 字符 转换所需的中间变量
	stringstream tmpR;
	string tmp;              //计算完成插入Fin 所需要的临时量
	string Lhs = lhs.strDigit;
	string Rhs =rhs.strDigit; 
	int lenL = Lhs.length();
	int lenR = Rhs.length();
	if(lenL < lenR)
	{
		Lhs.swap(Rhs);
		int tmp = lenL;
		lenL = lenR;
		lenR = tmp;
	}	
	string Fin ;
	//Fin.resize(0); //   sizeof(char) 最后会比计算结果长度多一个字节长度          Fin.resize(sizeof(0)); 丢失最后一个 , e = Fin.length()-1 会崩溃
	int i = 0 , j = 0 , k = 0;
	j = lenL-1;
	int R , L ,sum = 0;
	for(i = lenR-1 ; i >= 0; --i ,--j)
	{
		tmpR.clear();   //再重复使用时 , 要清空原有数据
		tmpL.clear();
		tmpR<<Rhs[i];   //字符 -> 数字
		tmpL<<Lhs[j];
		tmpR>>R;
		tmpL>>L;
		if(flag)
		{
			sum = L + R + 1;
		}
		else
		{
			sum = L + R;
		}
		if(sum >= 10)
		{
				flag = true;
				sum %= 10;
				tmpL.clear();
				tmpL << sum;  //数字 -> 字符
				tmpL >>tmp;
				Fin.insert(k++,tmp);  // 使用尾插               Fin.insert(0,tmp); 采用头插的方式 , 效率低  push_back(sum + '0');
		}
		else
		{
			flag = false;
			tmpL.clear();
			tmpL << sum;
			tmpL >>tmp;
			Fin.insert(k++,tmp);
		}
	}

	if(flag)  //较短字符串的最高位 相加 有无进位
	{
		if(i == j)   //如果两个数等长
		{
			tmpL.clear();
			tmpL<<1;
			tmpL>>tmp;
			Fin.insert(k++,tmp);
			//字符串反转
			reverse(Fin.begin(),Fin.end());
			return BigInt(Fin);
		}
		while(j >=0)  //有进位则写入
		{
			tmpL.clear();
			tmpL<<Lhs[j];
			tmpL>>L;
			sum = L + 1;
			if(sum >= 10)
			{
				flag = true;
				sum %= 10;
				tmpL.clear();
				tmpL << sum;
				tmpL >>tmp;
				Fin.insert(k++,tmp);
			}
			else
			{
				flag = false;
				break;
			}
			--j;
		}
		tmpL.clear();
		tmpL << sum;
		tmpL >>tmp;
		Fin.insert(k++,tmp);
		--j;
	}
	while(j >= 0)   //无进位 , 则摘抄
	{
		tmpL.clear();
		tmpL<<Lhs[j--];
		tmpL>>tmp;
		Fin.insert(k++,tmp);	
	}	
	//字符串反转
	reverse(Fin.begin(),Fin.end());
	
	i = 0;
	//结果全为零 , 则只显示一个
	while(Fin[i++] == '0')
	{	}
	if((i - 1) == k)
	{
		return (string)"0";
	}
	return BigInt(Fin);
}

// 大数减法
BigInt operator-(const BigInt &lhs, const BigInt &rhs)
{
	bool flag = false;      //借位
	bool mark = false;   //最终结果的正负   false -> 正 ; true -> 负
	stringstream tmpL;  // 数字 字符 转换所需的中间变量
	stringstream tmpR;
	string tmp;              //计算完成插入Fin 所需要的临时量
	string Lhs = lhs.strDigit;
	string Rhs =rhs.strDigit; 
	int lenL = Lhs.length();
	int lenR = Rhs.length();
	if(lenL < lenR)
	{
		Lhs.swap(Rhs);
		int tmp = lenL;
		lenL = lenR;
		lenR = tmp;
		mark = true;
	}	
	string Fin ;
	// Fin.resize(0); 
	int i = 0 , j = 0 , k = 0;
	j = lenL-1;
	int R , L ,dif = 0;
	for(i = lenR-1 ; i >= 0; --i ,--j)
	{
		tmpR.clear();   //再重复使用时 , 要清空原有数据
		tmpL.clear();
		tmpR<<Rhs[i];   //字符 -> 数字
		tmpL<<Lhs[j];
		tmpR>>R;
		tmpL>>L;
		if(flag)
		{
			dif = L - R - 1;
		}
		else
		{
			dif = L - R;
		}
		if(dif < 0 )
		{
			flag = true;
			dif += 10;
			tmpL.clear();
			tmpL<<dif;
			tmpL>>tmp;
			Fin.insert(k++,tmp);
		}
		else
		{
			flag = false;
			tmpL.clear();
			tmpL<<dif;
			tmpL>>tmp;
			Fin.insert(k++,tmp);
		}
	}

	if(flag)  //较短字符串的最高位 相加 有无借位
	{
		if(i == j)   //如果两个数等长
		{
			tmpL.clear();
			tmpL<<'-';
			tmpL>>tmp;
			Fin.insert(k++,tmp);
			reverse(Fin.begin(),Fin.end());
			return BigInt(Fin);
		}
		while(j >=0)  //有借位则写入
		{
			tmpL.clear();
			tmpL<<Lhs[j];
			tmpL>>L;
			dif = L - 1;
			if(dif < 0)
			{
				flag = true;
				dif += 10;
				tmpL.clear();
				tmpL << dif;
				tmpL >>tmp;
				Fin.insert(k++,tmp);
			}
			else
			{
				flag = false;
				break;
			}
			--j;
		}
		tmpL.clear();
		tmpL << dif;
		tmpL >>tmp;
		Fin.insert(k++,tmp);
		--j;
	}
	while(j >= 0)   //无借位 , 则摘抄
	{
		tmpL.clear();
		tmpL<<Lhs[j--];
		tmpL>>tmp;
		Fin.insert(k++,tmp);	
	}	
	//判断最终的符号
	if(mark)  
	{
		tmpL.clear();
		tmpL<<'-';
		tmpL>>tmp;
		Fin.insert(k++,tmp);
	}

	reverse(Fin.begin(),Fin.end());
	
	i = 0;
	//结果全为零 , 则只显示一个
	while(Fin[i++] == '0')
	{	}
	if((i - 1) == k)
	{
		return (string)"0";
	}
	return BigInt(Fin);
}

int main()
{
	BigInt int1("28937697857832167849697653231243");
	BigInt int2("9785645649886874535428765");
	BigInt int3("4785645649886874535428765");
	BigInt int4("12222222222222222222222222");
	BigInt int5("92222222222222222222222222");
	BigInt int7("92222222222222222222222222");
	cout <<  int2 + int1 << endl;
	cout <<  int1 - int2  << endl;

	cout <<  int4 + int5 << endl;
	cout <<  int3 - int2  << endl;
	cout <<  int2 - int1  << endl;
	cout <<  int5 - int7  << endl;
	
	return 0;
}

在代码中 , 在容器插入元素 , 可以使用insert()进行头插(效率太低) , 也可以使用push_banck()进行尾插(高效) ; 如果进行尾插的话 , 则要在最后进行字符串的逆置(泛型算法 reverse());而编者在写时 , 使用了insert() , 设置了一个 k 来记录插入位置(如果看过源码的话 , 不算析构和重新构造 , 其余代码insert和push_back是相同的,即 k 永远是最后元素的下一个位置 , 也算是尾插 );

你可能感兴趣的:(C++,C++)