高精度运算 C/C++

本文详细介绍了如何在C/C++中使用自定义结构体实现高精度计算,包括加法、减法、乘法和除法的模拟运算过程,以及相应的函数实现和注意事项。
摘要由CSDN通过智能技术生成

高精度运算

本文参考用C/C++实现基本高精度计算

一、为何要实现高精度

高精度的本质是有大数字参与的运算,由于有大数字的参与,在C/C++中,它超出数据类型例如long long等的表示范围,所以我们需要借用手动演算的方法来模拟运算。

二、准备阶段

首先,我们需要构造一个结构体来表示每一个大数字,用字符串表示数字的样子,用数组储存每一位上的数。

注: 数组需要倒序存储,即个位为a[0],十位为a[1]……

因为我们最初无法确定这个数到底有几位,为了方便运算,我们使用这样的存储方式。

构造完毕的结构体如下:

struct hp
{
    int a[maxn];
    char str[maxn];
    int len;
    void init() // 初始化
    {
        len = 0;
        sign = true;
        for(int i = 0; i < maxn; i++)
            a[i] = str[i] = 0;
    }
    void Init() // 带输入的初始化方法
    {
        scanf("%s",str);
        len = strlen(str);
        sign = true;
        for(int i = len - 1; i >= 0; i--)
            a[len - i - 1] = str[i] - '0';
    }
    void Print()
    {
        printf("%s\n",str);
    }
};

三、高精度加法

注意事项:

  1. 这里展示的加法只适用于正整数的相加。

  2. 运算时需要考虑进位。

接下来我们需要模拟以下图片的运算:

在这里插入图片描述

我们可以看到从个位开始,两个数开始相加,超过10之后就向前进位,直到最终计算完成。

显然,这样的运算所需要的次数是两个数中最大的位数。

现在,我们来模拟一下运算过程:

hp Add(const hp a, const hp b)
{
    hp ans;
    ans.init();
    int len = a.len > b.len ? a.len : b.len;
    for(int i = 0; i < len; i++)
    {
        ans.a[i] += a.a[i] + b.a[i];
        if(ans.a[i] >= 10) // 进位处理
        {
            int tmp = ans.a[i] / 10;
            ans.a[i] %= 10;
            ans.a[i + 1] += tmp;
        }
    }
    UpdateLen(ans); // 更新ans的长度
    UpdateStr(ans); // 更新ans的str以便输出
    return ans;
}

inline void UpdateLen(hp& a)
{
    int i;
    // 从高位开始数,确定位数,同时保证这个数为0时的长度为1
    for(i = maxn - 1; i > 0 && a.a[i] == 0; i--); 
    a.len = i + 1;
}

inline void UpdateStr(hp& a)
{
    for(int i = 0; i < a.len; i++)
        a.str[i] = a.a[a.len - i - 1] + '0';
}

这样就完成了加法的模拟。

四、高精度减法

注意事项:

  1. 我们不知道哪个数才是大的数,所以输出的结果可能为负,我们需要加上符号的判定。

    • 结构体增加符号
    bool sign; // 符号,记得更新构造函数的初始化
    
    • 增加比较大小的判定(该判定位于结构体内,也可以自行书写在外面)
    int Compare(const hp x)
    {
        if(len < x.len)
            return -1;
        else if(len > x.len)
            return 1;
        else
        {
            for(int i = len - 1; i >= 0; i--)
            {
                if(a[i] < x.a[i])
                    return -1;
                else if(a[i] > x.a[i])
                    return 1;
            }
        }
        return 0;
    }
    
    • 输出形式的修改
    void Print()
    {
        if(!sign)
            printf("-");
        printf("%s\n",str);
    }
    
  2. 需要进行借位操作的书写。

  3. 注意去掉高位运算出现的0。

模拟:

在这里插入图片描述

我们可以看到,有时最高位会出现为0的情况,我们使用的 UpdateLen 函数即可避免输出错误。

同样是从个位开始运算,接下来是代码:

hp Minus(hp a, hp b)
{
    hp ans;
    ans.init();
    if(a.Compare(b) == -1) // 大的在后,则输出负数,交换位置
    {
        hp tmp = a;
        a = b;
        b = tmp;
        ans.sign = false;
    }
    // 运算次数即为绝对值大的数的位数
    for(int i = 0; i < a.len; i++) 
    {
        ans.a[i] += a.a[i] - b.a[i];
        while(ans.a[i] < 0) // 借位
        {
            ans.a[i] += 10;
            ans.a[i + 1]--;
        }
    }
    UpdateLen(ans); // 更新长度,同时处理高位0的情况
    UpdateStr(ans);
    return ans;
}

五、高精度乘法

注意事项:

  1. 该版本未考虑为负数的情况,如果需要请自行添加。

  2. 该乘法实现需要在封装了加法的基础上进行运算。

先看手动模拟:

在这里插入图片描述

  • 不难看到,在第二个数的每一位跟第一个数相乘的时候,得出的数都进行了一个乘以 1 0 n 10^n 10n 的操作,即个位运算时乘以 1 0 0 10^0 100,十位运算时乘以 1 0 1 10^1 101……而这正好是存储这个数的数组的下标

  • 如果上面的描述难以理解,我们还可以通过乘法的分配律来理解:

123 × 456 = 123 × ( 400 + 50 + 6 ) = 123 × 400 + 123 × 50 + 123 × 6 123\times456 = 123\times(400 + 50 + 6) = 123\times400 + 123\times50 + 123\times6 123×456=123×(400+50+6)=123×400+123×50+123×6

  • 运算过程中恰好产生了第二个数的位数个的数,例如第二个数为456,则产生3个这样的数。我们先将这些数用数组储存起来,最后再统一进行相加的操作,这样乘法就完成了。

接下来是代码:

hp Multi(const hp a, const hp b)
{
    hp ans;
    hp *tmp = new hp[b.len]; // 创建数组用于储存
    ans.init();
    for(int i = 0; i < b.len; i++) // 初始化数组
        tmp[i].init();
    for(int i = 0; i < b.len; i++)
    {
        /*j代表中途产生的数的第j + 1位,a.a[j - i]、b.a[i]分
        别表示数字a、b中正在进行乘法运算的数,通过这样的错位来
        达到乘10^n的目的。例如,现在进行的是123*456中的123*50,
        此时i=1,若j=1,那么进行的就是3*5的运算,
        则计算结果就会直接存储在第1位及以后,而第0位本来就是0,
        我们就不需要管*/
        for(int j = i; j < i + a.len; j++)
            tmp[i].a[j] = a.a[j - i] * b.a[i];
        UpdateLen(tmp[i]); // 更新位数以便进行加法运算
    }
    for(int i = 0; i < b.len; i++)
        ans = Add(ans, tmp[i]); // 将全部加起来
    delete []tmp; // 记得释放申请的空间
    return ans;
}

六、高精度除法

注意事项:

  1. 该版本未考虑为负数的情况,如果需要请自行添加。

  2. 除法需要在封装了减法的基础上进行,同时需要比较大小的函数。

  3. 除法也存在高位0的情况。

还是先看模拟过程:

在这里插入图片描述

  • 可以看到,总共需要进行 4 − 1 + 1 = 4 4-1+1=4 41+1=4 次运算才能得到结果。其中,第一次对3进行了 1 0 4 − 1 10^{4-1} 1041 的操作, 1000 − 3000 1000-3000 10003000 为负数,则有效数字为0,余数为1000;第二次将3000变为了300, 1000 − 1200 1000-1200 10001200 为负数,则有效数字为3,余数为100,以此类推。

  • 那么如何进行将3变成3000,又变成300、30、3的操作呢,我们可以仿照乘法中的做法,直接在需要的位置上进行赋值操作即可。例如,现在要将3变成3000,那么我们就只需要在数组对应千位的地方赋值3即可。

对应代码如下:

hp Move(hp a, int x) // x正为左移,负为右移
{
    hp ans;
    ans.init();
    for(int i = 0; i < a.len + x; i++)
    {
        if(i - x >= 0)
            ans.a[i] = a.a[i - x];
    }
    UpdateLen(ans);
    UpdateStr(ans);
    return ans;   
}

hp Div(hp a, hp b)
{
    hp ans;
    ans.init();
    if(a.Compare(b) != -1) // 如果a<b,结果直接为0
    {
        int pow = a.len - b.len;
        hp cur = Move(b, pow);
        for(int i = 0; i < pow + 1; i++, cur = Move(cur, -1))
        {
            ans = Move(ans, 1); // 乘10
            // 一次一次的减,在结果为负的前一次停止
            while(a.Compare(cur) >= 0) 
            {
                a = Minus(a, cur);
                ans.a[0]++;
            } 
        }
    }
    UpdateLen(ans); // 更新位数,消除高位0
    UpdateStr(ans); // 更新字符串
    return ans; // 如果需要余数,那么此时a就是余数
}

七、完整代码

#include <cstdio>
#include <cstring>
const int maxn = 3e4 + 10;

struct hp
{
    bool sign;
    int a[maxn];
    char str[maxn];
    int len;
    void init()
    {
        len = 0;
        sign = true;
        for(int i = 0; i < maxn; i++)
            a[i] = str[i] = 0;
    }
    void Init()
    {
        scanf("%s",str);
        len = strlen(str);
        sign = true;
        for(int i = len - 1; i >= 0; i--)
            a[len - i - 1] = str[i] - '0';
    }
    void Print()
    {
        if(!sign)
            printf("-");
        printf("%s\n",str);
    }
    int Compare(const hp x)
    {
        if(len < x.len)
            return -1;
        else if(len > x.len)
            return 1;
        else
        {
            for(int i = len - 1; i >= 0; i--)
            {
                if(a[i] < x.a[i])
                    return -1;
                else if(a[i] > x.a[i])
                    return 1;
            }
        }
        return 0;
    }
};

inline void UpdateLen(hp& a)
{
    int i;
    for(i = maxn - 1; i > 0 && a.a[i] == 0; i--);
    a.len = i + 1;
}

inline void UpdateStr(hp& a)
{
    for(int i = 0; i < a.len; i++)
        a.str[i] = a.a[a.len - i - 1] + '0';
}

hp Add(const hp a, const hp b)
{
    hp ans;
    ans.init();
    int len = a.len > b.len ? a.len : b.len;
    int i;
    for(i = 0; i < len; i++)
    {
        ans.a[i] += a.a[i] + b.a[i];
        if(ans.a[i] >= 10)
        {
            int tmp = ans.a[i] / 10;
            ans.a[i] %= 10;
            ans.a[i + 1] += tmp;
        }
    }
    UpdateLen(ans);
    UpdateStr(ans);
    return ans;
}

hp Multi(const hp a, const hp b)
{
    hp ans;
    hp *tmp = new hp[b.len];
    ans.init();
    for(int i = 0; i < b.len; i++)
        tmp[i].init();
    for(int i = 0; i < b.len; i++)
    {
        for(int j = i; j < i + a.len; j++)
            tmp[i].a[j] = a.a[j - i] * b.a[i];
        UpdateLen(tmp[i]);
    }
    for(int i = 0; i < b.len; i++)
        ans = Add(ans, tmp[i]);
    return ans;
}

hp Minus(hp a, hp b)
{
    hp ans;
    ans.init();
    if(a.Compare(b) == -1)
    {
        hp tmp = a;
        a = b;
        b = tmp;
        ans.sign = false;
    }
    for(int i = 0; i < a.len; i++)
    {
        ans.a[i] += a.a[i] - b.a[i];
        while(ans.a[i] < 0)
        {
            ans.a[i] += 10;
            ans.a[i + 1]--;
        }
    }
    UpdateLen(ans);
    UpdateStr(ans);
    return ans;
}

hp Move(hp a, int x) // x正为左移,负为右移
{
    hp ans;
    ans.init();
    for(int i = 0; i < a.len + x; i++)
    {
        if(i - x >= 0)
            ans.a[i] = a.a[i - x];
    }
    UpdateLen(ans);
    UpdateStr(ans);
    return ans;   
}

hp Div(hp a, hp b)
{
    hp ans;
    ans.init();
    if(a.Compare(b) != -1)
    {
        int pow = a.len - b.len;
        hp cur = Move(b, pow);
        for(int i = 0; i < pow + 1; i++, cur = Move(cur, -1))
        {
            ans = Move(ans, 1);
            while(a.Compare(cur) >= 0)
            {
                a = Minus(a, cur);
                ans.a[0]++;
            } 
        }
    }
    UpdateLen(ans);
    UpdateStr(ans);
    return ans;
}

int main()
{
    hp a;
    a.Init();
    // b.Init();
    // Add(a, b).Print();
    int x;
    scanf("%d",&x);
    Move(a, x).Print();
    return 0;
}
  • 39
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值