C++实现大数模拟/高精度计算(包含加减乘除)详细版

C++实现大数模拟/高精度计算(包含加减乘除)详细版

加法

  • 加法很容易模拟,按照竖式计算一步一步来就可以
    • 首先要把数位都对齐,具体操作就是把两个数都用"0"补齐成同样的长度len,这里的len取的是a、b中较长的那个
    • 然后从右向左遍历,因为要从低位向高位逐位做加法
    • curSum记录当前数位的和,注意相加的时候要加上前一位的进位,进位用jinWei来保存
    • 因为是十进制加法,所以进位的计算方法为当前位的和整除10,保存在jinWei变量中
    • 当前位的最终结果是什么呢?当然是当前位的和对10取余(因为是十进制)
    • 如此循环,直到遍历完整个加数
    • 最后别忘了处理最高位的进位,直接把jinWei放到最终结果的左边即可
string bigAdd(string a,string b){
		string ans = "";
		//让两个数长度对齐 ,补零 
		int len = max(a.size(),b.size());
		for(int i = a.size();i < len;i++){
			a = "0" + a;
		}
		for(int i = b.size();i < len;i++){
			b = "0" + b;
		}
		//从后往前遍历两个数(因为已经对齐,可以同时遍历)
		int jinWei = 0;//进位初始为0 
		for(int i = len - 1;i >= 0;i--){
			int curSum = (a[i] - '0') + (b[i] - '0'); //当前位的和 
			curSum += jinWei;
			//十进制,满十进一
			jinWei = curSum / 10;
			curSum = curSum % 10;
			ans = to_string(curSum) + ans;
		}
		//处理最高位的进位 
		if(jinWei > 0){
			ans = to_string(jinWei) + ans;
		}
		return ans;
}

减法

  • 减法稍微复杂一点
    • 减法存在够不够减的问题,为了回避此问题,要保证a-b中a >= b。如果a<b了,就将a、b交换,再做减法,然后把结果前面加上负号即可。
    • 减法同样需要补齐长度,即在高位补"0"
    • 从后往前遍历,逐位相减,注意要把被减数减去上一位的借位,借位用变量jieWei保存
    • curMinus记录当前位的结果,要注意当当前位的被减数<当前位的减数时,要向高位借一位,借位位用变量jieWei记录
    • 借位的同时,将当前位的左数加10,再计算当前位的差
    • 如此循环,直至遍历完整个数
    • 最后,减法的差可能会出现前导零,需要手动去除,并考虑差为0的情况
string bigMinus(string a,string b){
	bool isSmall = false;
	//保证a>=b 
	if(a < b){
		swap(a,b);
		isSmall = true;	
	} 
	string ans = "";
	//补零
	int len = max(a.size(),b.size()) ;
	for(int i = a.size();i < len;i++){
		a = "0" + a;
	}
	for(int i = b.size();i < len;i++){
		b = "0" + b;
	}
	//从后往前遍历
	int jieWei = 0; //借位位初始为0 
	for(int i = len - 1;i >= 0;i--) {
		int curMinus = 0; //当前位的结果 
		int curLeft = (a[i] - '0') - jieWei; //当前位左数 
		int curRight = (b[i] - '0'); //当前位右数 
		//判断够不够减
		if(curLeft >= curRight){
			curMinus = curLeft - curRight;
			jieWei = 0;
		} else{ //不够减,借位 
			curMinus = 10 + curLeft - curRight;
			jieWei = 1;
		}
		ans = to_string(curMinus) + ans;
	}
	//去除前导零
	int zeroNum = 0;
	for(int i = 0;i < ans.size();i++) {
		if(ans[i] == '0'){
			zeroNum++;
		}else{
			break;
		}
	}
	ans.erase(ans.begin(),ans.begin() + zeroNum);
	//如果结果为0,则返回"0" 
	if(ans.size() == 0) return "0";
	if(isSmall) ans = "-" + ans;
	return ans;
} 

乘法

  • 乘法更加复杂一些,这里引用一下leetcode上大佬bigsai的文章
    • https://leetcode.cn/circle/article/Sy1x7o/

    • 大数乘法乍一想可能比较复杂,因为乘法比起加法可能进位不光是1,还有两个数各种位置都需要相乘计算,这时候就需要我们化繁为简了。

    • 多乘多考虑起来可能有些麻烦,但是如果多乘一考虑起来呢?如果是多位乘以一位数,那么就拿一位的分别乘以多位数的个位、十位、百位,在计算的同时考虑一下进位的情况。

  • 但是也可以先直接用int类型数组存储各位的乘积然后从右向左进行进位,如下图所示。
    在这里插入图片描述
  • 多乘多也是这个道理,将不同位乘积先叠加到对应位置上,然后从右向左进位,一直到不需要进位为止。
    在这里插入图片描述
  • 你可能会疑问,如果两个数组的长度分别为a和b这个数组到底该开多大呢?
    • a+b大小就够了,怎么分析呢?其中一个a不变。另一个b变成最小b+1数字即十的倍数,那么这样在相乘的时候也不过是a+b长度,所以这里a+b长度就够了。
  • 思路清晰以后,不难写出代码
string bigMultiply(string a,string b){
	//开辟数组value
	vector<int> value(a.size()+ b.size(),0);
	//b取出1位分别与a的每一位相乘 
	for(int bIndex = b.size() - 1;bIndex >= 0;bIndex--){
		int curB = b[bIndex] - '0';
		int valueIndex = value.size() - (b.size() - bIndex); //从b这一位开始填充 
		for(int aIndex = a.size() - 1;aIndex >= 0;aIndex--){
			int curA = a[aIndex] - '0';
			value[valueIndex] += curA * curB;
			valueIndex--;
		}
	}
	//处理进位 
	int jinWei = 0;
	for(int i = value.size() - 1;i >= 0;i--){
		int curValue =(value[i] + jinWei) % 10;
		jinWei = (value[i] + jinWei) / 10;
		value[i] = curValue;
	}
	string ans = "";
	bool isZero = true;
	for(int i = 0;i < value.size();i++){
		if(value[i] != 0){
			isZero = false;
		}
		if(isZero == false){
			ans = ans + to_string(value[i]);
		}
	}
	return ans;
	
}

除法

  • 除法反而简单
    • 只需要从前往后遍历,依次处理各个位上的数即可
    • 当前位的被除数curNum=上一位的余数remain * 10 + 当前位数字
    • 当前位的商curResult= 当前位的被除数curNum 整除 除数b
    • 当前位的余数remain = 当前位的被除数curNum % 除数b
    • 这样不用考虑够不够除,因为如果当前位不够除,则自动商0,那么在最后只需把结果的前导零去掉即可
    • 代码如下
string bigDivide(string a,int b,int &remain){
	//返回商,remain为余数 
	string ans = "";
	for(int i = 0;i < a.size();i++){
		int curNum = remain * 10 + (a[i] - '0'); //当前被除数
		int curResult = curNum / b; 
		ans = ans + to_string(curResult);
		remain = curNum % b;
	}
	//去除前导零
	bool zeroNum = 0;
	for(int i = 0;i < ans.size();i++) {
		if(ans[i] == '0'){
			zeroNum++;
		}else{
			break;
		}
	}
	ans.erase(ans.begin(),ans.begin() + zeroNum);
	return ans;
}

大数模拟本不难,只是没有人讲的通俗易懂,故尝试撰写此文

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值