C++ 的高精度除法

为什么需要高精度计算

对于 C++ 而言,最大的数据为 long long(64b,8位),对于超过 8B 的数据,C++ 没有对应的数据类型进行表示。所以我们需要知道高精度计算。更详细的解释,可以参考这个网页https://blog.csdn.net/justidle/article/details/104414459

高精度除法计算原理

在读小学时,我们做除法都采用竖式方法计算。被除数从高位开始,和被除数对齐,诸位“试商”,“试商”后被除数减去“试商”的数的乘积,如下图所示。

采用计算机做高精度除法时,模拟日常除法的步骤。但计算机不可能做“试商”,这时,我们可以采用减法来模拟。

试商过程

1、将除数移动和被除数对齐,位数不够时,补 0。

2、利用被除数减去除数,一直减到被除数小于除数,减的次数,就是“试商”的结果,每移动一次。

3、重复上述步骤,一直到被除数和除数的位数相等为止。

举例说明

举一个例子,比如 524134 除以 123,结果是 4261 ,余数为 31。

1、第一位 4 的来源是我们把 524 和 123 对齐,然后进行循环减法,循环了 4 次,余 32;

2、将 32134 的前三位 321 继续和 123 对齐,循环减法 2 次,余 75;

3、把 7534 的前三位 753 和 123 对齐,循环减法 6 次,余 15;

4、将 154 和 123 对齐,只能减 1 次,余 31。

所以 524134 除以 123,结果是 4261,余数为 31。

计算过程

  A B 余数
1 524134 123 4 32
2 032134 0123 2 75
3 007534 00123 6 15
4 000154 000123 1 31
5 000031 0000123   31

高精度除法实现

思路

1、定义存储数组。

2、读入数据处理。

3、试商过程。

4、删除前导 0 。所谓前导零,就是出现类似这样数据 01234,这个 0 实际是不需要的。

5、输出结果。倒序输出减法的结果数组 C,因为我们的个位是存储在下标为 0 的地方。

技术细节说明

定义存储数组

根据题目的要求定义数组。这个部分代码如下:

const int MAXN = 1e5+4; //根据题目的最大值。+4为了防止A+B出现进位
char s1[MAXN] = {};//存储字符串
char s2[MAXN] = {};//存储字符串
int a[MAXN] = {};//存储被除数
int b[MAXN] = {};//存储除数B
int c[MAXN] = {};//存储商
int tmp[MAXN] = {};

注意:这里的数组索引 0 所在的数据表示本数据的长度。

辅助函数

比较大小函数

用于比较两个 int 类型数组内数据大小。返回值和 C++ 库函数 campare() 相同。

int compare(int a[], int b[]) {
    //索引为0的数据为数组长度
    if (a[0]>b[0]) {
        return 1;
    } else if (a[0]<b[0]) {
        return -1;
    }

    //逐位比较
    for (int i=a[0]; i>0; i--) {
        if (a[i]>b[i]) {
            return 1;
        } else if (a[i]<b[i]) {
            return -1;
        }
    }

    return 0;
}

移位操作函数

void numcpy(int a[],int b[],int dest) {
    //将数组右移,使两个数组右端对齐,形参q数组储存右移后的结果
    for (int i=1;i<=a[0];i++) {
        b[i+dest-1] =a[i];
    }
    b[0] = a[0]+dest-1;
}

读入数据并处理

这里我们先考虑以下几种情况,即 15/4=3 ... 3、-8/3=-2 ... -2、10/(-3)=-3 ... 1 或者 -3*(-2)=1 ... -1,也就是说,需要对输入数据的正负号进行判断。这个部分代码如下:

    scanf("%s %s", s1, s2);//读入字符串

    //处理负数
    bool flaga = false;//乘数a的符号
    if ('-'==s1[0]) {
        flaga = true;
        strcpy(s1, &s1[1]);//删除负号
    }
    bool flagb = false;//乘数b的符号
    if ('-'==s2[0]) {
        flagb = true;
        strcpy(s2, &s2[1]);//删除负号
    }

    //处理输出的负号
    if (true==flaga && false==flagb) {
        //商为负数
        printf("-");
    }

    //处理乘数1
    int len = strlen(s1);
    a[0] = len;
    for (int i=0; i<len; i++) {
        a[len-i]=s1[i]-'0';
    }

    //处理乘数2
    len = strlen(s2);
    b[0] = len;
    for (int i=0; i<len; i++) {
        b[len-i]=s2[i]-'0';
    }

试商

这个部分代码如下:

    if (0==compare(a,b)) {
        //两数相等
        printf("1\n0\n");
        return 0;
    } else if (-1==compare(a,b)) {
        //被除数小,除数大
        printf("0\n");//输出除数
        if (true==flaga) {
            printf("-");
        }
        printf("%s\n", s1);
        return 0;
    } else {
        c[0] = a[0]-b[0]+1;
        for (int i=c[0]; i>0; i--) {
            memset(tmp, 0, sizeof(tmp));
            //高位对齐
            numcpy(b,tmp,i);

            //
            while (compare(a, tmp)>=0) {
                c[i]++;
                //减法
                for (int j=1; j<=a[0]; j++) {
                    if (a[j]<tmp[j]) {
                        a[j+1]--;
                        a[j]+=10;
                    }
                    a[j]-=tmp[j];
                }

                int k=a[0];
                while (a[k]==0) {
                    k--;
                }
                a[0]=k;
            }
        }

        //控制最高位的0
        while (c[0]>0 && c[c[0]]==0) {
            c[0]--;
        }
    }

输出计算结果

采用倒序的方式输出,因为我们数据保存是倒序结构,也就

    //逆序打印输出商和余数
    for (int i=c[0]; i>0; i--) {
        printf("%d", c[i]);
    }
    printf("\n");
    if (0==a[0]) {
        printf("0\n");
    } else {
        if (true==flaga) {
            printf("-");
        }
        for (int i=a[0]; i>0; i--) {
            printf("%d", a[i]);
        }
        printf("\n");
    }

输出格式需要根据实际题目要求进行修改。

例题和 AC 代码

题目

题目链接

一本通 OJ:http://ybt.ssoier.cn:8088/problem_show.php?pid=1308

我的OJ:http://47.110.135.197/problem.php?id=1213

题目描述

高精除以高精,求它们的商和余数。

输入

输入两个低于300位的正整数。

输出

输出商和余数。

样例输入

1231312318457577687897987642324567864324567876543245671425346756786867867867
1231312318767141738178325678412414124141425346756786867867867

样例输出

999999999748590
179780909068307566598992807564736854549985603543237528310337

分析

题目告诉我们不超过 300 位,也就是 MAXN = 300+4。

A 代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 300+4; //根据题目的最大值。+4为了防止A+B出现进位
char s1[MAXN] = {};//存储字符串
char s2[MAXN] = {};//存储字符串
int tmp[MAXN] = {};//交换用字符串
int a[MAXN] = {};//存储加数A
int b[MAXN] = {};//存储加数B
int c[MAXN] = {};//存储和B

int compare(int a[], int b[]) {
    //索引为0的数据为数组长度
    if (a[0]>b[0]) {
        return 1;
    } else if (a[0]<b[0]) {
        return -1;
    }

    //逐位比较
    for (int i=a[0]; i>0; i--) {
        if (a[i]>b[i]) {
            return 1;
        } else if (a[i]<b[i]) {
            return -1;
        }
    }

    return 0;
}

void numcpy(int a[],int b[],int dest) {
    //将数组右移,使两个数组右端对齐,形参q数组储存右移后的结果
    for (int i=1;i<=a[0];i++) {
        b[i+dest-1] =a[i];
    }
    b[0] = a[0]+dest-1;
}

int main() {
    scanf("%s %s", s1, s2);//读入字符串

    //处理负数
    bool flaga = false;//乘数a的符号
    if ('-'==s1[0]) {
        flaga = true;
        strcpy(s1, &s1[1]);//删除负号
    }
    bool flagb = false;//乘数b的符号
    if ('-'==s2[0]) {
        flagb = true;
        strcpy(s2, &s2[1]);//删除负号
    }

    //处理输出的负号
    if (true==flaga && false==flagb) {
        //商为负数
        printf("-");
    }

    //处理乘数1
    int len = strlen(s1);
    a[0] = len;
    for (int i=0; i<len; i++) {
        a[len-i]=s1[i]-'0';
    }

    //处理乘数2
    len = strlen(s2);
    b[0] = len;
    for (int i=0; i<len; i++) {
        b[len-i]=s2[i]-'0';
    }

    if (0==compare(a,b)) {
        //两数相等
        printf("1\n0\n");
        return 0;
    } else if (-1==compare(a,b)) {
        //被除数小,除数大
        printf("0\n");//输出除数
        if (true==flaga) {
            printf("-");
        }
        printf("%s\n", s1);
        return 0;
    } else {
        c[0] = a[0]-b[0]+1;
        for (int i=c[0]; i>0; i--) {
            memset(tmp, 0, sizeof(tmp));
            //高位对齐
            numcpy(b,tmp,i);

            //
            while (compare(a, tmp)>=0) {
                c[i]++;
                //减法
                for (int j=1; j<=a[0]; j++) {
                    if (a[j]<tmp[j]) {
                        a[j+1]--;
                        a[j]+=10;
                    }
                    a[j]-=tmp[j];
                }

                int k=a[0];
                while (a[k]==0) {
                    k--;
                }
                a[0]=k;
            }
        }

        //控制最高位的0
        while (c[0]>0 && c[c[0]]==0) {
            c[0]--;
        }
    }

    //逆序打印输出商和余数
    for (int i=c[0]; i>0; i--) {
        printf("%d", c[i]);
    }
    printf("\n");
    if (0==a[0]) {
        printf("0\n");
    } else {
        if (true==flaga) {
            printf("-");
        }
        for (int i=a[0]; i>0; i--) {
            printf("%d", a[i]);
        }
        printf("\n");
    }

    return 0;
}
发布了203 篇原创文章 · 获赞 101 · 访问量 104万+

猜你喜欢

转载自blog.csdn.net/justidle/article/details/104431257