高精度计算

所谓高精度计算,实际是就是对超过常规数据类型范围的数进行计算。

  •  高精度加法

输入部分:输入值的位数太大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';}

函数部分:

  1. 比较函数,判断当前所取的被除数的部分是否比num2(即除数)大,判断是否进行组合(如图样例中落下来的8和9组成98继续进行减法)
  2. 移动函数,将除数整体左移动(实际上是进行组合)
  3. 高精度减法部分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;
}

posted @ 2025-01-17 20:40  mc12356  阅读(9)  评论(0)    收藏  举报  来源