Angel_EN 2022-01-17 14:38 采纳率: 66.7%
浏览 105
已结题

我的高精度乘法运算算法 为什么位数不能超过105位?

进行高精度乘法运算时当数字输入位数超过大概105位时结果是错误的,此后随着输入位数的增加错误的位数也就越多,请问到底是怎么回事呢?
又该如何解决这个问题呢?
我的算法是模仿人类列竖式计算

下面是代码 乘法函数为BIgMultiply函数

#define _CRT_SECURE_NO_DEPRECATE
#pragma warning(disable:4996)    //使scanf()函数可用

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 205     //精度(数字位数) +2

//将字符串变为倒序
void reverse(char a[]) {
    static char a2[N];
    memset(a2, 0, sizeof(a2));
    int xa = strlen(a) - 1;
    int j = 0;

    for (int i = xa; i >= 0; i--) {
        a2[j] = a[i];
        j++;
    }
    memset(a, 0, sizeof(a));
    strcpy(a, a2);
}

//将字符串换算为真实数字
void str2numArry(char a[]) {
    for (int i = 0; a[i] != '\0'; i++) {
        a[i] -= '0';
    }
}

int strGetLongest(char a[], char b[]) {
    int xa = strlen(a);
    int xb = strlen(b);
    int longest = xb;
    if (xa >= xb) {
        longest = xa;
    }
    return longest;
}

//运算得出的out[]是逆序的

void BigPlus(char ia[], char ib[], char ResultGet[]) {
    static char out[N], a[N], b[N];
    int JW;//进位
    //初始化
    memset(out, 0, sizeof(out));
    memset(a, 0, sizeof(a));
    memset(b, 0, sizeof(b));
    memset(ResultGet, 0, sizeof(ResultGet));
    strcpy(a, ia);
    strcpy(b, ib);
    //先将数字倒序(因为列竖式时需要右对齐且从需要从右往左算)
    reverse(a);
    reverse(b);

    //取a,b之中最长的 长度?
    int longest = strGetLongest(a, b);
    //换算为真实数字
    str2numArry(a);
    str2numArry(b);
    //因为转换为真实数字之后末尾的‘0’会变成‘\0’,又因为数组经过倒序处理,所以先导零将变成字符串结尾 '\0' 将被忽略。
    //运算
    JW = 0;
    for (int i = 0; i <= longest; i++) {
        out[i] = a[i] + b[i] + JW;
        JW = 0;
        if (out[i] >= 10) {
            JW = out[i] / 10;
            out[i] %= 10;
        }
    }

    int j = 0;
    while (out[longest] == 0 && longest > 0) {
        longest--;
    }
    for (int i = longest; i >= 0; i--) {
        ResultGet[j] = out[i] + '0';

        j++;
    }
    ResultGet[j] = '\0';
}

void BigMinus(char ia[], char ib[], char ResultGet[]) {
    static char out[N], a[N], b[N];
    int JW;//借位

    //初始化
    memset(out, 0, sizeof(out));
    memset(a, 0, sizeof(a));
    memset(b, 0, sizeof(b));
    memset(ResultGet, 0, sizeof(ResultGet));
    strcpy(a, ia);
    strcpy(b, ib);
    //先将数字倒序(因为列竖式时需要右对齐且从需要从右往左算)
    reverse(a);
    reverse(b);

    //取a,b之中最长的 长度?
    int longest = strGetLongest(a, b);
    //换算为真实数字
    str2numArry(a);
    str2numArry(b);
    //因为转换为真实数字之后末尾的‘0’会变成‘\0’,又因为数组经过倒序处理,所以先导零将变成字符串结尾 '\0' 将被忽略。

    //运算
    JW = 0;
    for (int i = 0; i < longest; i++) {
        out[i] = a[i] - b[i] - JW;
        JW = 0;
        if (out[i] < 0) {
            JW = 1;
            out[i] += 10;
        }
    }

    //逆序输出ASCII
    int j = 0;

    while (out[longest] == 0 && longest > 0) {
        longest--;
    }
    for (int i = longest; i >= 0; i--) {
        ResultGet[j] = out[i] + '0';

        j++;
    }
    ResultGet[j] = '\0';
}

void BigMultiply(char ia[], char ib[], char ResultGet[]) {
    static char  out1[N][2 * N], a[N+84], b[N];
    static long long int out[2 * N];
    long long int JW;//进位
    //初始化
    memset(out, 0, sizeof(out));
    memset(out1, 0, sizeof(out1));
    memset(a, 0, sizeof(a));
    memset(b, 0, sizeof(b));
    memset(ResultGet, 0, sizeof(ResultGet));
    strcpy(a, ia);
    strcpy(b, ib);

    //先将数字倒序(因为列竖式时需要右对齐且从需要从右往左算)
    reverse(a);
    reverse(b);

    int la = strlen(a);
    int lb = strlen(b);
    int longest = strGetLongest(a, b) + 1;//中间过程结果长度可能的最大值
    int longest_out = la + lb;//结果可能的最长长度
    //换算为真实数字
    str2numArry(a);
    str2numArry(b);
    //因为转换为真实数字之后末尾的‘0’会变成‘\0’,又因为数组经过倒序处理,所以先导零将变成字符串结尾 '\0' 将被忽略。
    //运算
    JW = 0;

    for (int i = 0; i < lb; i++) {
        for (int k = 0, m = i; k < longest + i; k++, m++) {//数组序列是从零开始的,用<
            out1[i][m] = b[i] * a[k] + JW;
            JW = 0;
            if (out1[i][m] >= 10) {
                JW = out1[i][m] / 10;
                out1[i][m] %= 10;
            }
            
            printf("%d ", out1[i][k]);
        }
        putchar('\n');
    }
    putchar('\n');
    putchar('\n');

    
    //char test[50][50] = { {1,2,3},{1,1,1},{1,1,1} };
    //lb是实际列数
    for (int i = 0; i < longest_out; i++) {
        out[i] = out1[0][i];
    }
    
    for (int i = 1; i < lb; i++) {
        JW = 0;
        for (int j = 0; j <= longest_out; j++) {
            out[j] += out1[i][j] + JW;
            //if(out[j] < 0 || out[j] > 9223372036854775000) exit(0);//test
            JW = 0;
            if (out[j] >= 10) {
                JW = out[j] / 10;
                out[j] %= 10;
            }
            //if (out[j] > 10) exit(0);//test
            //if (JW > 10) exit(0);//test
        }

        
    }



    //working
    int j = 0;
    while (out[longest_out] == 0 && longest_out > 0) {
        longest_out--;
    }
    for (int i = longest_out; i >= 0; i--) {
        ResultGet[j] = out[i] + '0';
        //printf("%d",out[i]);
        j++;
    }
    ResultGet[j] = '\0';
}
int main() {
    //int T;
    static char a[N], b[N], out[2 * N];
    /*
    scanf("%d", &T);
    for (int i = 1; i <= T; i++) {
        scanf("%s%s", a, b);
        BigPlus(a, b, out);
        printf("Case %d:\n%s + %s = %s\n\n", i, a, b, out);
    }
    */
    while (1) {
        scanf("%s%s", a, b);
        BigMultiply(a, b, out);
        printf("%s", out);
        putchar('\n');
    }

    return 0;
}

  • 写回答

2条回答 默认 最新

  • 於黾 2022-01-17 14:57
    关注

    你如果要模拟列竖式,那你的所有中间结果也都应该用char[]来存啊,不要依赖long long int类型来保存中间结果,否则输入大到一定程度,中间结果溢出了

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 1月25日
  • 已采纳回答 1月17日
  • 修改了问题 1月17日
  • 创建了问题 1月17日

悬赏问题

  • ¥15 自动转发微信群信息到另外一个微信群
  • ¥15 outlook无法配置成功
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换