C语言实现大数计算

2020/01/02 15:18
阅读数 31

所谓大数,就是超过longlong表示的位数。

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define BASE (10)
#define MAX(x, y) ((x) > (y) ? (x) : (y))

typedef struct  bignumber_s
{
    char sign;
    int len;
    char data[];
}bignumber_s;

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b);
bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b);

bignumber_s *make_bignumber_temp(int len, int sign)
{
    bignumber_s *temp = malloc(sizeof(bignumber_s) + len);
    if (NULL == temp)
    {
        perror("Malloc");
        exit(-1);
    }
    temp->sign = sign;
    temp->len = len;
    memset(temp->data, 0, len);
    return temp;
}

const char *strip_str(const char *str)
{
    int i = 0;
    int len = strlen(str);
    for (; i < len - 1 && str[i] == '0'; i++)
    {
        ;
    }
    return str+i;
}

void fill_data_fromstr(bignumber_s *n, const char *str)
{
    int i = 0;
    int len = n->len;
    for (; i < len; i++)
    {
        int d = str[len - 1 - i] -'0';
        if (d >= 0 && d <= 9)
        {
            n->data[i] = d;
        }
        else
        {
            fprintf(stderr, "Invalid Number:%s\n", str);
            exit(-1);
        }
    }
}

bignumber_s *make_bignumber_fromstr(const char *str)
{
    int sign = 0;
    if (str[0] == '-')
    {
        sign = 1;
        str++;
    }

    const char *striped_str = strip_str(str);
    int len = strlen(striped_str);
    bignumber_s *temp = make_bignumber_temp(len, sign);
    fill_data_fromstr(temp, striped_str);
    return temp;
}

void print_bignumber(bignumber_s *b)
{
    int len = b->len;
    char *str = malloc(len+1);
    int i = 0;

    for(; i < len; i++)
    {
        str[i] = b->data[len-i-1] + '0';
    }
    str[len] = '\0';

    fprintf(stdout, "%s%s\n", b->sign == 1 ? "-":"", strip_str(str));
    free(str);
}

void usage(const char *s)
{
    fprintf(stderr, "Usage:%s number +-x/ number2.\n", s);
    exit(-1);
}

void add_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
    int i = 0;
    char carry = 0;
    int len = r->len;
    for(; i < len; i++)
    {
        if (i < a->len)
        {
            carry += a->data[i];
        }
        if (i < b->len)
        {
            carry += b->data[i];
        }
        r->data[i] = carry % BASE;
        carry /= BASE;
    }
}

bignumber_s *calc_add(bignumber_s *a, bignumber_s *b)
{
    if (a->sign == b->sign)
    {
        int len = MAX(a->len, b->len) + 1;
        bignumber_s *result = make_bignumber_temp(len, a->sign);
        add_impl(a, b, result);
        return result;
    }
    else if (a->sign == 0 && b->sign == 1)
    {
        b->sign = 0;
        return  calc_sub(a, b);
    }
    else if (a->sign == 1 && b->sign == 0)
    {
        a->sign = 0;
        return calc_sub(b, a);
    } 
}

void sub_impl(bignumber_s *a, bignumber_s *b, bignumber_s *r)
{
    int i = 0;
    int borrow = 0;
    int len = r->len;
    int temp = 0;
    for (i = 0; i < len; i++)
    {
        temp = a->data[i] + BASE - borrow - ((i < b->len) ? b->data[i]:0);
        r->data[i] = temp % BASE;
        borrow = temp/BASE ? 0 : 1;
    }
}

int valid_len(bignumber_s *a)
{
    int len = a->len;
    int i = len - 1;
    for (i = len - 1; i >= 0; i--)
    {
        if(a->data[i] == 0)
        {
            len--;
        }
        else
        {
            break;
        }        
    }
    return len;
}

int cmp(bignumber_s *a, bignumber_s *b)
{
    if (a->sign == 0 && b->sign == 1)
    {
        return 1;
    }
    if (a->sign == 1 && b->sign == 0)
    {
        return -1;
    }

    int sign = a->sign;
    int alen = valid_len(a);
    int blen = valid_len(b);
    
    if (alen > blen)
    {
        return (sign == 1 ? -1 : 1);
    }
    else if (alen < blen)
    {
        return (sign == 1 ? 1 : -1);
    }
    else
    {
        int i = 0;
        int len = alen;
        for (i = len - 1; i >= 0; i--)
        {
            if (a->data[i] > b->data[i])
            {
                return (sign == 1? -1:1);
            }
            else if (a->data[i] < b->data[i])
            {
                return (sign == 1 ? 1 : -1);
            }
        }
        return 0;
    }
}

bignumber_s *calc_sub(bignumber_s *a, bignumber_s *b)
{
    if (a->sign == 0 && b->sign == 0)
    {
        if (cmp(a, b) >= 0)
        {
            int len = a->len;
            bignumber_s *result= make_bignumber_temp(len, 0);
            sub_impl(a, b, result);
            return result;
        }
        else
        {
            int len = b->len;
            bignumber_s *result = make_bignumber_temp(len, 1);
            sub_impl(b, a, result);
            return result;
        }
    }
    else if (a->sign == 1 && b->sign == 1)
    {
        b->sign = 0;
        a->sign = 0;
        return calc_sub(b, a);
    }
    else if (a->sign == 0 && b->sign == 1)
    {
        b->sign = 0;
        bignumber_s *result = calc_add(a, b);
        return result;
    }
    else if (a->sign == 1 && b->sign == 0)
    {
        a->sign = 0;
        bignumber_s *result = calc_add(a, b);
        result->sign = 1;
        return result;
    }
}

void mul_impl(bignumber_s *x, bignumber_s *y, bignumber_s *z)
{
    int n = x->len;
    int m = y->len;
    int i = 0;
    int j = 0;
    int carry = 0;

    for (; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            carry += y->data[i] * x->data[j] + z->data[i+j];
            z->data[i+j] = carry % BASE;
            carry /= BASE;
        }

        for (; j + i < m + n; j++)
        {
            carry += z->data[i+j];
            z->data[i+j] = carry % BASE;
            carry /= BASE;
        }
    }
}

bignumber_s *calc_mul(bignumber_s *a, bignumber_s *b)
{
    bignumber_s *zero = make_bignumber_temp(1,0);
    if (cmp(a, zero) == 0 || cmp(b, zero) == 0)
    {
        return zero;
    }
    int len = a->len + b->len;
    bignumber_s *result = make_bignumber_temp(len, a->sign == b->sign ? 0 : 1);
    mul_impl(a, b, result);
    return result;
}

void plusone(bignumber_s *a)
{
    int len = a->len;
    int i;
    int carry = 1;
    for (i = 0; i < len; i++)
    {
        carry += a->data[i];
        a->data[i] = carry % BASE;
        carry /= BASE;
    }
}

bignumber_s *calc_div(bignumber_s *a, bignumber_s *b)
{
    bignumber_s *zero = make_bignumber_temp(1, 0);
    if (cmp(b, zero) == 0)
    {
        fprintf(stderr, "Interfer division by zero\n");
        exit(-1);
    }
    else if (cmp(a, zero) == 0)
    {
        return zero;
    }

    int len = a->len;
    bignumber_s *result = make_bignumber_temp(len, a->sign == b->sign ? 0 : 1);
    a->sign = 0;
    b->sign = 0;
    bignumber_s *temp = make_bignumber_temp(len, 0);
    bignumber_s *aa = a;
    while (1)
    {
        if (cmp(aa, b) >= 0)
        {
            sub_impl(aa, b, temp);
            plusone(result);
            aa = temp;
        }
        else
        {
            free(temp);
            return result;
        }
    }   
}

int main(int argc, char *argv[])
{
    char str1[1024] = {0};
    char operator[3] = {0};
    char str2[1024] = {0};
    //scanf("%s %s %s", str1, operator, str2);
    scanf("%s", str1);
    scanf("%s", operator);
    scanf("%s", str2);
    bignumber_s *a = make_bignumber_fromstr(str1);
    bignumber_s *b = make_bignumber_fromstr(str2);

    if (0 == strcmp(operator, "+"))
    {
        print_bignumber(calc_add(a, b));
    }
    else if (0 == strcmp(operator, "-"))
    {
        print_bignumber(calc_sub(a, b));
    }
    else if (0 == strcmp(operator, "x"))
    {
        print_bignumber(calc_mul(a, b));
    }
    else if (0 == strcmp(operator, "/"))
    {
        print_bignumber(calc_div(a, b));
    }
    else
    {
        usage(argv[0]);
    }
    return 0;
}

 

在求商的运算中,如果商的值太大,就会出现运算时间很长的情况。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部