C++高精度
写在开头:
本文主要实现的是自然数范围内做运算的 加法、减法、乘法、除法 四种基本高精度运算以及依靠减法和乘法实现的大数 阶乘 和利用字符串比较实现的 比较 运算(如果有需要可以自行加判断将范围扩充到整数范围内)
实现思路
我们用字符串去储存大数,让各个函数可以处理这些字符串进行模拟运算并返回一个用字符串表示的大数
C++高精度 之 加法运算:
大数加法运算的模拟相对简单,我们直接模拟竖式加法即可。我们把对应位相加并将结果存入一个定义好长度的数组(放在对应位处),我们可以先不必考虑每一位的进位情况,直接存数即可。当我们算完得到结果数组后,我们从最后一位(个位)开始检查进位情况,如果有进位我们调整该位及其高一位的数。当我们遍历完整个数组后再将数组转化成字符串返回即可。
C++高精度 之 减法运算:
大数减法整体思想依旧是加法的模拟竖式,不同点是我们需要判断一下被减数与减数的大小,我们保证被减数大于减数如果不满足我们交换两个字符串数并在答案字符串中存入一个负号("-")。然后我们模拟竖式加法,把对应位相减的结果存入一个定义好长度的数组的对应位处(不考虑每一位的借位情况),然后从个位开始检查借位,如果有借位我们调整该位及其高一位的数,最后转化数组为字符串并返回。
C++高精度 之 乘法运算:
乘法运算相对需要一些技巧,但仍旧是模拟竖式乘法,仍旧是不考虑进位,依次将每位相乘结果存入数组对应位处(这里需要累加存入,做竖式乘法我们知道每一位最后算结果时,不一定是两位相加有可能是多位),然后依旧是从个位开始检查进位,最后转化数组并返回结果。
动画演示大数乘法运算的模拟过程:
C++高精度乘法模拟动画
C++高精度 之 除法运算:
因为是自然数范围内的除法我们模拟不出精确的小数结果,所以我们的函数最后需要返回两个值一个是商一个是余数。除法的模拟运算我们用到了大数减法,思想依旧是模拟竖式的除法:
- 我们从最高位依次取数直到大于等于除数为止
- 然后我们用目前取到的数减去若干个除数(直到减到小于除数为止)
- 然后我们记录减去了几个除数,最后将此数存入答案字符串
- 我们将记录清零然后再从被除数中再取一个数,判断是否小于除数
- 如果小于则直接将0加到答案字符串末尾
- 否则我们转入步骤 2 然后将最后记录加到答案字符串末尾
- 直到我们将被除数中的数取完为止
动画演示大数除法运算的模拟过程:
C++高精度除法模拟动画
C++代码实现:
#include<iostream>
#include<vector>
using namespace std;
//......................................................................................
int myCompare(string a,string b)//大数比较设定a>b返回1 a<b返回-1 a=b返回0
{
if(a.size()!=b.size())//如果字符串长度不同那么长度大的那个就是更大的大数
{
if(a.size()>b.size())
return 1;
else
return -1;
}
else //如果字符串长度相同我们通过直接比较字符串的字典序来判断大小
{
if(a>b)
return 1;
else if(a==b)
return 0;
else
return -1;
}
}
//......................................................................................
string myAdd(string a,string b) //高精度加法
{
int n=max(a.size(),b.size())+1;
vector<int>ans(n,0);//开辟一个足够存储结果的数组,结果的位数最多为 位数最多的那个数的位数加一(考虑进位)
int i=a.size()-1,j=b.size()-1,k=n-1;//从个位开始通过模拟竖式进行大数加法
while(i>=0&&j>=0)//当两数均未加完时
{
ans[k--]=(a[i--]-'0')+(b[j--]-'0');//我们让我们的数组存储每一位相加的结果注意将字符串转为整型数字
}
//检测是否有某个数的高位未加完将其直接存入数组中
while(j>=0)
{
ans[k--]=(b[j--]-'0');
}
while(i>=0)
{
ans[k--]=(a[i--]-'0');
}
string c="";//创建一个字符串去存储答案
for(int i=n-1;i>0;i--)//因为之前的竖式加每一位都没考虑进位所以我们从最后开始检查进位
{//因为是加法如果有进位最多也就进一
if(ans[i]>=10)//如果大于10说明应该进位那么我们让此位减10它的高一位加1
{
ans[i]-=10;
ans[i-1]++;
}
c.insert(0,1,ans[i]+'0');//处理后的结果转化为字符插入结果字符的首位
}
if(ans[0]>0)//检查最最高位是否大于0如果两数相加没有进位那么这一位就是0我们就不必存储它否则则放入字符串
{
c.insert(0,1,ans[0]+'0');
}
return c;//返回答案
}
//......................................................................................
string mySubtract(string a,string b)//高精度减法(整体思想同加法)
{
string c=""; //创建一个字符串去存储答案
if(myCompare(a,b)==0) //先比较一下两数大小如果相等我们直接返回0即可
return "0";
if(myCompare(a,b)==-1)//如果a<b那么我们交换两数的值同时在答案字符串中先放入一个负号
{
swap(a,b);
c.push_back('-');
}
int n=a.size();
//开辟一个足够存储结果的数组 减法结果的位数最多为位数最多的那个数的位数我们保证了a为的大数所以就是a的位数
vector<int>ans(n,0);
int i=a.size()-1,j=b.size()-1,k=n-1;//从个位开始模拟竖式减法
int t=0;//表示低位的借位情况 0:无借位 1:有借位
while(i>=0) //同加法一样模拟运算我们知道a是大数所以a遍历完竖式才完成
{
char nowb;//被减数当前位有数就正常减 没有数说明就是0
if(j>=0) nowb=b[j];
else nowb='0';
ans[k]=a[i]-nowb-t;//对应位相减同时减去低位的借位
if(ans[k]<0)//如果结果小于0 则向高位借一位
{
t=1;
ans[k]+=10;
}
else t=0; //否则向高位无借位
k--,i--,j--; //继续判断高一位
}
bool flag=true;//这里与加法不同高位可能出现多个零开头的情况我们需要找到第一不是零的地方再存数
for(int i=0;i<n;i++)
{
if(flag&&ans[i]==0)//如果当前数为0且未存数则不执行存数操作
continue;
flag=false; //一旦存入数更改标志位
c.push_back(ans[i]+'0');
}
return c; //返回结果
}
//......................................................................................
string myMultiply(string a,string b)//高精度乘法
{
if(a=="0"||b=="0") //其中有一个为0时直接返回0
return "0";
vector<int>ans; //开辟一个足够存储结果的数组
int n=a.size(),m=b.size();
ans.resize(n+m,0); //乘法结果的位数最多为两数的位数之和
for(int i=0;i<n;i++) //这里从高位开始和从低位开始无所谓我们将两数相乘的结果不考虑放到对应位上最后去检测进位即可
{ //例如个位乘百位的结果 以及十位乘十位的结果 都放到百位上
for(int j=0;j<m;j++)
{
ans[i+j+1]+=(a[i]-'0')*(b[j]-'0');
}
}
for(int i=n+m-1;i>0;i--) //我们从低位开始检查进位
{
if(ans[i]>=10) //如果大于10说明有进位但乘法的进位不一定只进1
{
ans[i-1]+=(ans[i]/10);//高位加地位的高于个位部分
ans[i]%=10; //低位对十求余
}
}
string c =""; //创建字符串存储答案
bool flag=true; //同减法一样找到第一个不是0的位置开始存数
for(int t=0;t<n+m;t++)
{
if(flag&&ans[t]==0)
continue;
flag=false;
c.push_back(ans[t]+'0');
}
return c; //返回答案
}
//......................................................................................
vector<string>myDivide(string a,string b)//高精度除法
{
vector<string>ans(2,"0");//先创建两个字符串空间去存储答案一个存商一个存余数
if(myCompare(a,b)==-1) //如果被除数比除数小商为0余数为a返回答案即可
{
ans[1]=a;
return ans;
}
else if(myCompare(a,b)==0)//如果被除数与除数相等商为1余数为0返回答案即可
{
ans[0]="1";
return ans;
}
else //否则我们需要模拟除法的竖式来进行计算
{
string res="";//创建存储商的字符串
int m1=a.size(),m2=b.size();
string a1=a.substr(0,m2-1);
for(int i=m2-1;i<m1;i++) //模拟竖式从高位开始依次取数减去除数能减几个该位商的当前位就是几
{
if(a1=="0") //如果a1为0为了防止a1出现0开头的情况我们将它清空
a1="";
a1.push_back(a[i]); //我们从被除数中取一个数放入a1继续减
int e=0;
while(myCompare(a1,b)>=0)//当a1大于等于除数时便一直减同时e累加
{
e++;
a1=mySubtract(a1,b);
}
if(res.size()||e) //如果res不为空或者e不为0我们存储他
res.push_back(e+'0');
}
ans[0]=res; //最后res就是商
ans[1]=a1; //a1就是余数
return ans; //返回答案
}
}
//......................................................................................
string myFactorial(string a)//高精度阶乘
{/*我们还可以利用高精度减法和乘法实现大数的阶乘(最大可运行出10000左右的阶乘)*/
if(a=="1")
return a;
else
return myMultiply(a,myFactorial(mySubtract(a,"1")));
}
//......................................................................................
int main()
{
string a,b;
string add_ans,subtract_ans,multiply_ans,factorial_ans;
vector<string>divide_ans;
int compare_ans;
cin>>a>>b;
compare_ans=myCompare(a,b);
cout<<compare_ans<<endl;
add_ans=myAdd(a,b);
cout<<add_ans<<endl;
subtract_ans=mySubtract(a,b);
cout<<subtract_ans<<endl;
multiply_ans=myMultiply(a,b);
cout<<multiply_ans<<endl;
divide_ans=myDivide(a,b);
cout<<divide_ans[0]<<endl<<divide_ans[1]<<endl;
// factorial_ans=myFactorial(a);
// cout<<factorial_ans<<endl;
return 0;
}