高精度计算
所谓高精度计算,实际是就是对超过常规数据类型范围的数进行计算。
-
高精度加法
输入部分:输入值的位数太大long long也无法储存,所以使用数组来模拟数值(每一个数组元素代表数值的一位数),采用字符数和fgets结合可以简单的使用strlen获取数的位数且易于后续倒序(输入值左边是最高位,进行加法计算的时候从低位开始,所以将数颠倒过来,方便计算,且进位只需加长数组,更加方便)[使用cin会更加简便,详情见高精度乘法]
char numa[5000],numb[5000];
int num1[5000],num2[5000];
fgets(numa,5000,stdin);
fgets(numb,5000,stdin);//end in \n
if(numa[strlen(numa)-1]=='\n'){numa[strlen(numa)-1]='\0';}
if(numb[strlen(numb)-1]=='\n'){numb[strlen(numb)-1]='\0';}
int len1=strlen(numa);
int len2=strlen(numb);
for(int i=len1-1,j=0;i>=0;i--,j++){num1[j]=numa[i]-'0';}
for(int i=len2-1,j=0;i>=0;i--,j++){num2[j]=numb[i]-'0';}
计算部分:各位相加进位,最后处理最高位的特殊情况,使用最大长度可以避免前导0的问题(数字第一位总不能是0吧)
int len=len1>len2?len1:len2;
for(int i=0;len2>i;i++) {
num1[i]+=num2[i];
if(num1[i]>=10){num1[i]-=10;num1[i+1]++;}
}
if(num1[len-1]>=10){num1[len-1]-=10;num1[len]++;}
if(num1[len]>0){len++;}
输出部分 :再次倒序恢复正常数字顺序
for(int i=len-1;i>=0;i--) {cout<<num1[i];}
总代码
#include<iostream>
#include<cstring>
using namespace std;
int main() {
char numa[5000],numb[5000];
int num1[5000],num2[5000];
fgets(numa,5000,stdin);
fgets(numb,5000,stdin);//end in \n
if(numa[strlen(numa)-1]=='\n'){numa[strlen(numa)-1]='\0';}
if(numb[strlen(numb)-1]=='\n'){numb[strlen(numb)-1]='\0';}
int len1=strlen(numa);
int len2=strlen(numb);
for(int i=len1-1,j=0;i>=0;i--,j++){num1[j]=numa[i]-'0';}
for(int i=len2-1,j=0;i>=0;i--,j++){num2[j]=numb[i]-'0';}
int len=len1>len2?len1:len2;
for(int i=0;len2>i;i++) {
num1[i]+=num2[i];
if(num1[i]>=10){num1[i]-=10;num1[i+1]++;}
}
if(num1[len-1]>=10){num1[len-1]-=10;num1[len]++;}
if(num1[len]>0){len++;}
for(int i=len-1;i>=0;i--) {cout<<num1[i];}
return 0;
}
-
高精度减法
输入部分:同高精度加法,倒序读取
char numa[5000],numb[5000];
int num1[5000],num2[5000];
fgets(numa,5000,stdin);
fgets(numb,5000,stdin);//end in \n
if(numa[strlen(numa)-1]=='\n'){numa[strlen(numa)-1]='\0';}
if(numb[strlen(numb)-1]=='\n'){numb[strlen(numb)-1]='\0';}
int len1=strlen(numa);
int len2=strlen(numb);
for(int i=len1-1,j=0;i>=0;i--,j++){num1[j]=numa[i]-'0';}
for(int i=len2-1,j=0;i>=0;i--,j++){num2[j]=numb[i]-'0';}
比较部分: 减法可能会产生负数,需要提前比较两数大小,首先比较位数易得大小关系,若位数相同再逐位比较
int compare(int len1,int len2,int num1[],int num2[]) {
if(len1>len2){return 1;}
else if(len1==len2) {
for(int i=len1-1;i>=0;i--) {
if(num1[i]>num2[i]) {return 1;}
else if(num1[i]<num2[i]) {return 0;}
}
return 1;
}
else {return 0;}
}
计算部分:进行逐位减法并进行 “借1”
if(compare(len1,len2,num1,num2)) {
for(int i=0;i<len1;i++) {
num1[i]-=num2[i];
if(num1[i]<0){num1[i]+=10;num1[i+1]--;}
}
}
else {
cout<<'-';
for(int i=0;i<len2;i++) {
num2[i]-=num1[i];
if(num2[i]<0){num2[i]+=10;num2[i+1]--;}
}
}
输出部分:分类输出并处理符号问题和前导0问题(数字不以0为最高位)
if(compare(len1,len2,num1,num2)) {
for(int i=len1-1;num1[i]==0;i--) {len1--;}
for(int i=len1-1;i>=0;i--) {
cout<<num1[i];
}
}
else {
cout<<'-';
for(int i=len2-1;num2[i]==0;i--) {len2--;}
for(int i=len2-1;i>=0;i--) {
cout<<num2[i];
}
}
总代码:
#include<iostream>
#include<cstring>
using namespace std;
int compare(int len1,int len2,int num1[],int num2[]) {
if(len1>len2){return 1;}
else if(len1==len2) {
for(int i=len1-1;i>=0;i--) {
if(num1[i]>num2[i]) {return 1;}
else if(num1[i]<num2[i]) {return 0;}
}
return 1;
}
else {return 0;}
}
int main() {
char numa[5000],numb[5000];
int num1[5000],num2[5000];
fgets(numa,5000,stdin);
fgets(numb,5000,stdin);//end in \n
if(numa[strlen(numa)-1]=='\n'){numa[strlen(numa)-1]='\0';}
if(numb[strlen(numb)-1]=='\n'){numb[strlen(numb)-1]='\0';}
int len1=strlen(numa);
int len2=strlen(numb);
for(int i=len1-1,j=0;i>=0;i--,j++){num1[j]=numa[i]-'0';}
for(int i=len2-1,j=0;i>=0;i--,j++){num2[j]=numb[i]-'0';}
if(compare(len1,len2,num1,num2)) {
for(int i=0;i<len1;i++) {
num1[i]-=num2[i];
if(num1[i]<0){num1[i]+=10;num1[i+1]--;}
}
for(int i=len1-1;num1[i]==0;i--) {len1--;}
for(int i=len1-1;i>=0;i--) {
cout<<num1[i];
}
}
else {
cout<<'-';
for(int i=0;i<len2;i++) {
num2[i]-=num1[i];
if(num2[i]<0){num2[i]+=10;num2[i+1]--;}
}
for(int i=len2-1;num2[i]==0;i--) {len2--;}
for(int i=len2-1;i>=0;i--) {
cout<<num2[i];
}
}
return 0;
}
-
高精度乘法
根据乘法计算原理可得c[z]=a[x]*b[y],x+y=z。
a2 a1 a0
* b1 b0
___________________________________
a2b0 a1b0 a0b0
a2b1 a1b1 a0b1
___________________________________
c3 c2 c1 c0
输入部分: 采用c++的cin简便读取字符串
cin>>numa>>numb;
int len1=strlen(numa);
int len2=strlen(numb);
for(int i=len1-1,j=0;i>=0;i--,j++){num1[j]=numa[i]-'0';}
for(int i=len2-1,j=0;i>=0;i--,j++){num2[j]=numb[i]-'0';}
计算部分:根据计算原理进行乘法(先计算最后统一进位)
for (int i=0;i<len1;i++) {
for(int j=0;j<len2;j++) {
num3[i+j]+=num1[i]*num2[j];
}
}
for(int i=0;i<len1+len2;i++) {
if(num3[i]>9) {num3[i+1]+=num3[i]/10;num3[i]%=10;}
}
输出部分:去除前导0并倒序输出乘积
int len=len1+len2;
for(int i=len-1;i>0&&num3[i]==0;i--) {len--;}
for(int i=len-1;i>=0;i--) {cout<<num3[i];}
总代码:
#include<iostream>
#include<cstring>
using namespace std;
char numa[5000],numb[5000];
int num1[5000],num2[5000],num3[25000000];
int main() {
cin>>numa>>numb;
int len1=strlen(numa);
int len2=strlen(numb);
for(int i=len1-1,j=0;i>=0;i--,j++){num1[j]=numa[i]-'0';}
for(int i=len2-1,j=0;i>=0;i--,j++){num2[j]=numb[i]-'0';}
for (int i=0;i<len1;i++) {
for(int j=0;j<len2;j++) {
num3[i+j]+=num1[i]*num2[j];
}
}
for(int i=0;i<len1+len2;i++) {
if(num3[i]>9) {num3[i+1]+=num3[i]/10;num3[i]%=10;}
}
int len=len1+len2;
for(int i=len-1;i>0&&num3[i]==0;i--) {len--;}
for(int i=len-1;i>=0;i--) {cout<<num3[i];}
return 0;
}
-
高精度除法
除法实际上是多次减法

商位数的知识

输入部分:将两个数倒序,位数小的在前面补0,倒序后右对齐,即最高位对齐
cin>>numa>>numb;
int len1=strlen(numa);
int len2=strlen(numb);
int end2=len1-len2;//end2为num2个位数所在的索引(num2最左侧)
int k=0;//用于处理商
for(int i=len1-1,j=0;i>=0;i--,j++){num1[j]=numa[i]-'0';}
for(int i=len2-1,j=end2;i>=0;i--,j++){num2[j]=numb[i]-'0';}
函数部分:
- 比较函数,判断当前所取的被除数的部分是否比num2(即除数)大,判断是否进行组合(如图样例中落下来的8和9组成98继续进行减法)
- 移动函数,将除数整体左移动(实际上是进行组合)
- 高精度减法部分subtraction(减法)
int compare(int num1[],int num2[],int len1,int end2) {
for(int j=len1-1;j>=end2;j--) {
if(num1[j]>num2[j]) {return 1;}
else if(num1[j]<num2[j]) {return 0;}
}
return 1;//exact equality防止遗漏完全相等
}
void move(int num2[],int end2,int len2) {
for(int i=end2;i<=end2+len2;i++) {
num2[i-1]=num2[i];
}
}
void subtraction(int num1[],int len1,int num2[],int end2,int len2) {
for(int i=end2;i<len1;i++) {
num1[i]-=num2[i];
if(num1[i]<0){num1[i]+=10;num1[i+1]--;}
}
}
计算部分:
判断,如果大于就减法(使用result存储商,用k记录商的位数),小于就移动(组合),当end2(除数的个位数的索引)<0的时候无法进行组合,模拟除法结束
while(end2>=0) {
if(compare(num1,num2,len1,end2)) {
subtraction(num1,len1,num2,end2,len2);
result[k]++;
}
else {move(num2,end2,len2);end2--;k++;}
}
输出部分:int一个m来处理商的前导0,特判被除数小于除数的情况,处理余数的前导零并倒序输出
int m=0;
for(;result[m]==0&&m<k;){m++;}
for(int i=m;i<k;i++) {cout<<result[i];}
if (k==0){cout<<"0";}//被除数小于除数特判
cout<<endl;
for(int i=len1-1;num1[i]==0&&i>0;i--){len1--;}
for(int i=len1-1;i>=0;i--) {cout<<num1[i];}
总代码:
#include<iostream>
#include<cstring>
using namespace std;
char numa[5000],numb[5000];
int num1[5000],num2[5000],result[5000];
int compare(int num1[],int num2[],int len1,int end2) {
for(int j=len1-1;j>=end2;j--) {
if(num1[j]>num2[j]) {return 1;}
else if(num1[j]<num2[j]) {return 0;}
}
return 1;//exact equality防止遗漏完全相等
}
void move(int num2[],int end2,int len2) {
for(int i=end2;i<=end2+len2;i++) {
num2[i-1]=num2[i];
}
}
void subtraction(int num1[],int len1,int num2[],int end2,int len2) {
for(int i=end2;i<len1;i++) {
num1[i]-=num2[i];
if(num1[i]<0){num1[i]+=10;num1[i+1]--;}
}
}
int main() {
cin>>numa>>numb;
int len1=strlen(numa);
int len2=strlen(numb);
int end2=len1-len2;//end2为num2个位数所在的索引(num2最左侧)
int k=0;//用于处理商
for(int i=len1-1,j=0;i>=0;i--,j++){num1[j]=numa[i]-'0';}
for(int i=len2-1,j=end2;i>=0;i--,j++){num2[j]=numb[i]-'0';}
while(end2>=0) {
if(compare(num1,num2,len1,end2)) {
subtraction(num1,len1,num2,end2,len2);
result[k]++;
}
else {move(num2,end2,len2);end2--;k++;}
}
int m=0;
for(;result[m]==0&&m<k;){m++;}
for(int i=m;i<k;i++) {cout<<result[i];}
if (k==0){cout<<"0";}//被除数小于除数特判
cout<<endl;
for(int i=len1-1;num1[i]==0&&i>0;i--){len1--;}
for(int i=len1-1;i>=0;i--) {cout<<num1[i];}
return 0;
}

浙公网安备 33010602011771号