高精度计算

前几日看了刘汝佳算法竞赛入门经典有关高精度计算的章节,实在是云里雾里,不知所云。什么设计一个结构体bign来储存高精度非负整数,重载bign常用运算符,甚至都还用到了指针,看了都十分头疼,我想对于新手来说还是有些难于理解的。其实完全不需要那些模板函数,只要理解了高精度是如何计算的,我们完全可以想用的的时候临时写一个,也耗不了多少时间。

之所以会产生高精度计算,是由于计算机储存数字大小是有限的,即使是unsigned long long型也只能储存0~2^64-1的自然数,然而有时候我们的需求量远远不局限于此。有可能我们需要计算上百位数的加减乘除。这时候高精度计算就派上用场了。

上小学时,老师便教会我们如何计算加减乘除,现在让我们回到过去,以小学生的思维来思考如何进行高精度计算。

先介绍高精度的加法运算:

首先我们得用字符串去存储数据,为了方便计算,我们再把字符串中的每个数存储到数组中,数组先初始化为0,存储时最好反向存储,这样对每位进行加法运算时,小位在前,大位在后,正想遍历即可。做加法运算时逢十进一,是不是和小学生做算术题一样?的确,就这么简单!

计算结束后还没有完,我们还得保证大位上不会出现0打头,比方说一个数不能是007856,我们得把前面的0给舍去转化成7856,这也是很关键的一步。

好吧,话不多说,上代码就能更加明了了。

#include <iostream>
#include <cstdio>
#include <cstring>
#define MAX_NUM 205        //定义所开数组的大小,尽量开的大一些
using namespace std;

int main()
{
    int num_1[MAX_NUM], num_2[MAX_NUM],length_1,length_2;
    char str_1[MAX_NUM], str_2[MAX_NUM];            //定义字符串,使待运算数存储到字符串中
    scanf("%s%s", str_1, str_2);
    memset(num_1, 0, sizeof(num_1));
    memset(num_2, 0, sizeof(num_2));            //初始化两个数组
    length_1 = strlen(str_1);
    length_2 = strlen(str_2);           //求出字符串的长度
    for(int i = 0; i < length_1; i++)
    {
        num_1[i] = str_1[length_1 - i - 1] - '0';        //数组反向存储字符串中的每位数字
    }
    for(int i = 0; i < length_2; i++)
    {
        num_2[i] = str_2[length_2 - i - 1] - '0';
    }
    for(int i= 0; i < MAX_NUM; i++)
    {
        num_1[i] += num_2[i];              //各位数相加
        if(num_1[i] >= 10)
        {
            num_1[i] %= 10;                 //若相加超过10,取余
            num_1[i+1]++;    //下一位先加1
        }
    }
    bool isBegin = false;              //定义一个变量跳出多余的0
    for(int i = MAX_NUM - 1; i >= 0; i--)
    {
        if(isBegin)
            printf("%d",num_1[i]);
        else if(num_1[i])
        {
            printf("%d",num_1[i]);
            isBegin = true;       //碰到非零值,确定开始输出
        }
    }
    if(!isBegin)
        printf("0");        //千万不要漏了只有0的情况
    return 0;
}

用一个int来存储一位数有时候太浪费了,我们可以让数组的每个元素存储4位数,为什么存储4位而不是存储6位甚至7位呢,原因在于当我们做乘法运算时,两个4位数相乘可以达到8位,如果我们定义存储的数太大,乘积就会溢出。

代码和上面的长不多,稍有改动。

#include <iostream>
#include <cstdio>
#include <cstring>
#define MAX_NUM 205

int main()
{
    int num_1[MAX_NUM], num_2[MAX_NUM],length_1,length_2;
    char str_1[MAX_NUM], str_2[MAX_NUM];
    scanf("%s%s", str_1, str_2);

    memset(num_1, 0, sizeof(num_1));
    memset(num_2, 0, sizeof(num_2));
    length_1 = strlen(str_1);
    length_2 = strlen(str_2);
    int j;
    for(int i = 0; i < MAX_NUM; i++)
    {

        j=0;
        if(length_1 - j - 1 >= 0)
        num_1[i] += str_1[length_1 - j - 1] - '0';
        if(length_1 - j - 2 >= 0)
        num_1[i] += (str_1[length_1 - j - 2] - '0') * 10;
        if(length_1 - j - 3 >= 0)
        num_1[i] += (str_1[length_1 - j - 3] - '0') * 100;
        if(length_1 - j - 4 >= 0)
        num_1[i] += (str_1[length_1 - j - 4] - '0') * 1000;
        length_1 -= 4;
        if(length_1 <= 0)
                break;
    }
    for(int i = 0; i < MAX_NUM; i++)
    {

        j=0;
        if(length_2 - j - 1 >= 0)
        num_2[i] += str_2[length_2 - j - 1] - '0';
        if(length_2 - j - 2 >= 0)
        num_2[i] += (str_2[length_2 - j - 2] - '0') * 10;
        if(length_2 - j - 3 >= 0)
        num_2[i] += (str_2[length_2 - j - 3] - '0') * 100;
        if(length_2 - j - 4 >= 0)
        num_2[i] += (str_2[length_2 - j - 4] - '0') * 1000;
        length_2 -= 4;
        if(length_2 <= 0)
                break;
    }
    for(int i= 0; i < MAX_NUM; i++)
    {
        num_1[i] += num_2[i];
        if(num_1[i] >= 10000)
        {
            num_1[i] %= 10000;
            num_1[i+1]++;
        }
    }
    bool isBegin = false;
    for(int i = MAX_NUM - 1; i >= 0; i--)
    {
        if(isBegin)
            printf("%04d",num_1[i]);    //保证4位输出
        else if(num_1[i])
        {
            printf("%d",num_1[i]);   //第一组数不需要4位输出,否则会有前导0
            isBegin = true;
        }
    }
    if(!isBegin)
        printf("0");
    return 0;
}

高精度乘法运算:

计算过程基本上和小学生列竖式做乘法相同,为编程方便,并不急于处理进位,而是将进位问题留待最后统一处理。具体处理方法见图:


#include <iostream>
#include <cstdio>
#include <cstring>
#define MAX_NUM 205 //定义所开数组的大小,尽量开的大一些
using namespace std;

int main()
{
    int num_1[MAX_NUM], num_2[MAX_NUM],length_1,length_2,result[MAX_NUM * 2];
    char str_1[MAX_NUM], str_2[MAX_NUM];             //定义字符串,使待运算数存储到字符串中
    scanf("%s%s", str_1, str_2);
    memset(num_1, 0, sizeof(num_1));
    memset(num_2, 0, sizeof(num_2));
    memset(result,0,sizeof(result));                    //初始化数组
    length_1 = strlen(str_1);
    length_2 = strlen(str_2);             //求出字符串的长度
    for(int i = 0; i < length_1; i++)
    {
        num_1[i] = str_1[length_1-i-1] - '0';                //数组反向存储字符串中的每数字
    }
    for(int i = 0; i < length_2; i++)
    {
        num_2[i] = str_2[length_2-i-1] - '0';
    }
    for(int i = 0; i < length_1; i++)
        for(int j = 0; j < length_2; j++)
        {
            result[i + j] += num_1[i] * num_2[j];           //计算各位相乘并加上原来该位的数
        }
    for(int i = 0; i < MAX_NUM * 2; i++)
    {
        if(result[i] >= 10)
        {
            result[i + 1] += result[i]/10;                  //下一位加上进位值
            result[i] %= 10;               //大于10求余
        }
    }
    bool isBegin = false;                       //定义一个变量跳出多余的0
    for(int i = MAX_NUM * 2 - 1; i >= 0; i--)
    {
        if(isBegin)
            printf("%d",result[i]);
        else if(result[i])
        {
            printf("%d",result[i]);
            isBegin = true;           //碰到非零值,确定开始输出
        }
    }
    if(!isBegin)
        printf("0");         //千万不要漏了只有0的情况
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值