大数的加减乘除法的实现(2)
大数的乘法
思路
参照普通整型的乘法的实现过程。其中一个因数的每一数位上的数字分别乘以另一个因数的每一个数位上的数字,把进位的数值记录下来,加到更高的数位上,只记录积的最低一位的数值作为本位的数值。
难点:本程序的最难点在于,因数数位上的对齐,且每一次的乘积的最低位的控制,用以下的图来说明。
因数b的数位上的每一位乘以因数a的数位上的每一位(a:0~3;b:0~1)
,相乘的积用ans来存储。显然,a、b的积的最低位是a的数位加上b的数位,即图中的i+j
。i=0,j=0时
,a[j]=4,b[i]=7,a[j]*b[i]=28,2作为更高一位的进位,记录下来。8作为ans[i+j]的数值。i=0,j=1时
,a[j]=3,b[i]=7,a[j]*b[i]=21,21再加上前一位的进位2,为23;2作为更高一位的进位,记录下来。3作为ans[i+j]的数值。以此类推,当j到达4时,a[i]不能乘了,但前一位还可能有进位,要加在ans[i+j]上,且进位置零,作为下一次的进位。i=1,j=0,再开始相乘。
ans的处理:ans要将对应的位数的积相加,作为最终的得数,在这里并不是将全部的积求出来了再相加,而是采用了a[j]与b[i]乘一次,ans[i+j]相加一次。ans除了要加上a[j]与b[i]相乘的积的最低位外,还要加上本身的数值和前一位的进位。当ans[i+j]>'9'时
,要进位一次,ans[i+j]减少10。
代码实现如下
//大数的乘法
//以字符串的形式存储数据
#include<iostream>
#include<string>
#include<algorithm>
#include<sstream>
using namespace std;
void i2s(int n,string &str){ //整型转换成字符型函数
stringstream stmp;
stmp << n;
stmp >>str;
}
string multiplicative(string a,string b){
if(a.find_first_not_of('0')==string::npos||b.find_first_not_of('0')) //判断a,b是否等于0,其中一个等于0,返回0
return "0";
//a,b均不等于0,进行相乘
a=a.substr(a.find_first_not_of('0')); //a,b去掉前面的0
b=b.substr(b.find_first_not_of('0'));
reverse(a.begin(),a.end()); //a,b翻转,低位在左侧,并对齐
reverse(b.begin(),b.end());
string ans(a.length()+b.length(),'0'); //ans表示积
int numtmp=0; //numtmp:表示前一位的进位
int num=0; //每两位的积
int anstmp=0; //ans前一位的进位
int i,j;
for(i=0;i<b.length();i++){ //b的每一个数位乘以a
for(j=0;j<a.length();j++){ //乘以a的每一个数位
num=(a[j]-'0')*(b[i]-'0');
num+=numtmp;
ans[j+i]=ans[j+i]+num%10+anstmp; //ans的值的处理
anstmp=0; //ans已进位,须清零
numtmp=num/10;
while(ans[j+i]>'9'){ //ans大于’9‘,有进位
ans[j+i]=ans[j+i]-10;
anstmp++; //ans进位给后一位
}
}
ans[j+i]=ans[j+i]+anstmp+numtmp; //a已遍历完了,处理前一位的进位
anstmp=0; //ans进位清零
numtmp=0; //每一个b[j]与a[0]相乘,前一位的进位为零
}
reverse(ans.begin(),ans.end()); //ans翻转回来
return ans.substr(ans.find_first_not_of('0')); //ans去掉前面的零
}
int main(){
string a,b;
cin>>a>>b; //输入两个因数
cout<<multiplicative(a,b)<<endl; //输出积
return 0;
}
大数的除法
思路
除法主要转换为减法
来实现。
比如:求a/b
整数部分:
输入a与b的约束:
若a等于0,即被除数为0,商一定为0;
若b等于0,即除数为0,商无意义;
若a>=b,则用a去减b,可以减几次b,则整数为几。
小数部分:
若a等于,剩下的部分皆为0;
若a<b,则在a后补0,再去减b;
循环执行,直到达到了要求的精确度。
代码实现如下
//大数除法的实现,并可实现精确到多少位小数, 精确方法采用截尾法
#include<iostream>
#include<algorithm>
#include<string>
#include <sstream>
using namespace std;
string subtract(string a,string b){ //减法函数
bool flag=false; //标记a,b的比较,a<b为true,结果为负
if( a.length()<b.length() || a.length()==b.length()&&a<b){ //如果b>a,交换
flag=true;
string tmp = b; //a,b交换
b=a;
a=tmp;
}
//翻转,低位在左边,先运算
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
//按位做减法
for(int i=0;i<b.length();++i){
if(a[i]>=b[i]){ //本位够减
a[i]=a[i]-b[i]+'0';
}else{ //不够减,需借位
int k=1;
while(a[i+k]=='0'){ //如果下一位只有0,借出1,置9,再往后一位借1
a[i+k]='9';
k++;
}
a[i+k]=a[i+k]-'1'+'0'; //i+k位借出了1
a[i]=(a[i]-'0')+10-(b[i]-'0')+'0'; //i位借来了10,相减
}
}
reverse(a.begin(),a.end()); //翻转回来
if(a.find_first_not_of('0')==string::npos) //string::npos指如果没找到,返回一个标志;这里指;如果a全部为零,则进入
return "0";
if(flag) { //负数的处理
string ans(a.length()+1,'0'); //声明一个长度为a.length()+1,字符全部为‘0’的字符串
ans[0]='-'; //加上负号
for(int i=a.length()+1;i>0;i--){ //将a赋值给ans,a的第1位放到ans的第2位,以此类推 。。。
ans[i]=a[i-1];
} //字符串前面全部去掉零
return ans.substr(ans.find_first_not_of('0'));
}
return a.substr(a.find_first_not_of('0'));
}
void i2s(int n, string &str) { //将整型转换成字符型
stringstream ss; //字符流
ss << n;
ss >> str;
}
bool compare(string a,string b){ //比较a,b;若a>=b;返回true
return (a.length()>b.length()||a.length()==b.length()&&a>=b);
}
string divide(string a,string b,long long count){ //除法函数
if(a.find_first_not_of('0')==string::npos){ //a字符串全为0,即被除数为0
a="0.";
for(int i=0;i<count;i++){ //精确到count位小数
a.append("0");
}
return a;
}
else a=a.substr(a.find_first_not_of('0')); //去掉a字符串前面的零
if(b.find_first_not_of('0')==string::npos) //b字符串全为0,即除数为0,无意义
return "ERROR";
else b=b.substr(b.find_first_not_of('0'));
//整数部分的处理
int t=0; //t:保存a可以减多少次b,即a/b的商
string t_str="0"; //t_str是t的字符串形式
while(compare(a,b)){ //若a>=b
a=subtract(a,b);
t++;
}
i2s(t,t_str); //将整型t转换成字符型t_str
string ans=t_str; //ans存储a/b的商
ans.append("."); //加上小数点
//小数部分的处理
for(int i=0;i<count;i++){ //count:表示截尾精确到count位小数
if(a.find_first_not_of("0")==string::npos) //若a字符全为0
ans.append("0");
else{
a.append("0"); //a补0
t=0;
t_str="0";
while(compare(a,b)){
a=subtract(a,b);
t++;
}
i2s(t,t_str); //t表示a可以减多少次b,t_str是t的字符串形式
ans.append(t_str); //再加上一位小数
}
}
return ans;
}
int main(){
string a,b;
long long count;
cout<<"请输入被除数:";
cin>>a;
cout<<"请输入除数:";
cin>>b;
cout<<"请输入精确位数:";
cin>>count;
cout<<divide(a,b,count)<<endl;
return 0;
}
注意点
- 乘法、除法程序只可处理整数,不可处理负数。
- 除法中使用的减法函数使用了上一篇博客中的减法实现程序,其中考虑到了,a<b的情况,但除法中的减法函数不需要用到a<b的情况,可根据实际要求更改一下。
- 以上仅仅是个人的见解,如还有其他情况未考虑到,希望大佬们可以批评指出,感谢!