寒假集训一期(5)——高精度(上)加法与减法

提示:高精度解决的问题非常简单,也就是a+b,a-b,a*b,a/b,它的原理也很简单,就是模拟我们在数学上做的运算,一步一步的进位,退位,代码大约有40行(c++),另外python自带高精度,不用手写


前言

这一篇博文讲的是加法和减法,下一篇博文主要讲乘法和除法

一、a + b

首先,我们来回顾一下,在数学中a+b到底是怎么运算的,假设 114 +1987,

数学中的运算

第一步:对准个位
第二步:从低位至高位运算
第三步:4 + 7 = 11,11 / 10 = 1,进位为1,11 % 10 = 1,在ans变量中记录1
第四步:1 + 8 +进位1 = 10,10 /10 = 1,进位为1,10 % 10 = 0,在1之前记录0
第五步: 1 + 9 + 进位1 = 11, 11 / 10 = 1,进位为1,11 % 10 = 1,在0之前记录1
第六步: 1 + 进位 1 = 2 ,2 / 10 = 0,无进位,2 % 10 = 2,在1之前记录2
第七步: ans = 2101

编程中呢

在编程语言中,我们很难做到对齐个位,就算能,也会很费时间,这个时候,我们采用了一种翻转思想,即将两个加数翻转相加,再把和翻转过来即可得到正确答案,我们可以用到rev函数,来实现翻转

void rev(string s, int a[], int &len){//翻转函数
	len = s.size();					  //获取原长度
	for(int i = 0; i <= len - 1; i++)
		a[i] = s[len - i - 1] - '0';  //循环依次将调换顺序
}

实现了翻转后,我们就可以开始做加法了:

从高位开始,对齐相加,记录进位,然后在每一次的相加过程中改变进位,以此类推即可

代码:

void add(int a[], int b[], int c[]){  //函数传参的知识参见我的上一篇博文
	lenc = max(lena, lenb);           //和的长度
	int jw = 0;						  //定义进位变量
	for(int i = 0; i <= lenc; i++){
		int tmp = a[i] + b[i] + jw;   //第一个加数 + 第二个加数 + 进位
		c[i] = tmp % 10; 			  //tmp % 10即为当前数字
		jw = tmp / 10;				  //除以10即为当前进位
	}
	return ;                          //void不用返回,可以不写此行
}

然后在主函数里一次调用这两个函数

int main(){
	ios::sync_with_stdio(false);      //这是读入优化
	cin >> s1 >> s2;
	rev(s1, a, lena);
	rev(s2, b, lenb);
	add(a, b, c);
}

最后,输出的时候,我们要注意两个问题:

1.记得倒序输出
2.记得去掉高位0

首先去0:

	while(c[lenc] == 0 && lenc > 0) lenc--; 

最后倒序输出

while(lenc >= 0) cout << c[lenc--];

组合起来就是完整代码了

#include <bits/stdc++.h>
using namespace std;
const int M = 5005;
string s1, s2;
int a[M], b[M], c[M], lena, lenb, lenc;
void rev(string s, int a[], int &len){//翻转函数
	len = s.size();					  //获取原长度
	for(int i = 0; i <= len - 1; i++)
		a[i] = s[len - i - 1] - '0';  //循环依次将调换顺序
}
void add(int a[], int b[], int c[]){
	lenc = max(lena, lenb);           //和的长度
	int jw = 0;						  //定义进位变量
	for(int i = 0; i <= lenc; i++){
		int tmp = a[i] + b[i] + jw;   //第一个加数 + 第二个加数 + 进位
		c[i] = tmp % 10; 			  //tmp % 10即为当前数字
		jw = tmp / 10;				  //除以10即为当前进位
	}
	return ;
}
int main(){
	ios::sync_with_stdio(false);
	cin >> s1 >> s2;
	rev(s1, a, lena);
	rev(s2, b, lenb);
	add(a, b, c);
	while(c[lenc] == 0 && lenc > 0) lenc--;
	while(lenc >= 0) cout << c[lenc--];
}

二、a - b

和a+b不同的是,a-b要考虑退位,而且较为复杂,

void sub(int a[], int b[], int c[]){
	lenc = lena;           //和的长度
	int jw = 0;						  //定义进位变量
	for(int i = 0; i <= lenc - 1; i++){
		int tmp = a[i] - b[i] - jw;   //第一个加数 - 第二个加数 - 进位
		if(tmp < 10){				  
			c[i] = tmp + 10;
			jw = 1;
		}else{
			c[i] = tmp;
			jw = 0;
		}
	}
	return ;
}

首先要看第一个加数 - 第二个加数 - 进位的值是否小于10,如果小于10,说明要记录退位,因为不可能有小于20的情况,所以退位只能为1,否则没有退位,记录为0.

还有一个不同,a-b需要考虑负数的情况
也就是说,如果a<b,我们要转换成b-a然后在前面输出一个“-”号,判断的时候因为输入的是字符串,不能直接作比较,只能通过比较长度来比较,如果长度相等,就可以用字典序直接比较两个字符串的大小,如下:

if((s1.size() == s2.size() && s1 < s2) || (s2.size() > s1.size())){
		cout << '-';
		string s3 = s1;
		s1 = s2;
		s2 = s3;//这里用到了三变量交换法
	}

其余就和加法没什么不同了,给出完整代码:

#include <bits/stdc++.h>
using namespace std;
const int M = 5005;
string s1, s2;
int a[M], b[M], c[M], lena, lenb, lenc;
void rev(string s, int a[], int &len){//翻转函数
	len = s.size();					  //获取原长度
	for(int i = 0; i <= len - 1; i++)
		a[i] = s[len - i - 1] - '0';  //循环依次将调换顺序
}
void sub(int a[], int b[], int c[]){
	lenc = lena;           //和的长度
	int jw = 0;						  //定义进位变量
	for(int i = 0; i <= lenc - 1; i++){
		int tmp = a[i] - b[i] - jw;   //第一个加数 + 第二个加数 + 进位
		if(tmp < 10){				  
			c[i] = tmp + 10;
			jw = 1;
		}else{
			c[i] = tmp;
			jw = 0;
		}
	}
	return ;
}
int main(){
	ios::sync_with_stdio(false);
	cin >> s1 >> s2;
	if((s1.size() == s2.size() && s1 < s2) || (s2.size() > s1.size())){
		cout << '-';
		string s3 = s1;
		s1 = s2;
		s2 = s3;
	}
	rev(s1, a, lena);
	rev(s2, b, lenb);
	sub(a, b, c);
	while(c[lenc] == 0 && lenc > 0) lenc--;
	while(lenc >= 0) cout << c[lenc--];
}

总结

不管是加法运算还是减法运算,都用到了模拟的思想,可见模拟十分重要,在你做题时没有思绪时,不妨用用模拟来实现一下。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来自八中的小鹿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值