提示:高精度解决的问题非常简单,也就是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--];
}
总结
不管是加法运算还是减法运算,都用到了模拟的思想,可见模拟十分重要,在你做题时没有思绪时,不妨用用模拟来实现一下。