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;
}
大数模拟本不难,只是没有人讲的通俗易懂,故尝试撰写此文