高精度(仅在C++)

170 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

大整数(高精度整数)概念、创建、输入、表示和四则运算(高精度的加减乘除)——附完整代码_51CTO博客_整数用什么表示

大整数:高精度整数,其含义是基本数据类型无法存储其精度的整数。

整数的高位存储在数组的高位,整数的低位存储在数组的低位。不反过来的原因是,在运算的时候都是从整数的低位到高位进行枚举,顺位存储和这种思维相合。

例如:235813 存储在数组中,即有d[0] = 3,d[1]= 1,d[2] = 8, d[3] = 5, d[4] =3, d[5] = 2;

注意: 把整数按字符串%s读入的时候,实际上是逆位存储的,即str[0] =‘2’, str[3] = ‘3’,str[5]= ‘3’,因此在读入之后,需要另存为至d[]数组的时候反转一下

为了方便获取大整数的长度,一般会定义一个int类变量len来记录其长度,并和d数组合成结构体:

Java有大整数类,没有位数要求

Python默认数范围无限大

仅C++需要

  1. A+BA+B,A和B的位数大约是10610^6
  2. ABA-B,A和B的位数大约是10610^6
  3. A×aA \times a,大整数乘以小整数,A位数小于等于10610^6,a的数值小于等于10910^9,这里a可能更小一些,a≤10000
  4. A÷aA \div a,求商和余数
  5. A×BA \times B,偶尔会考,次数不是很多
  6. A÷BA \div B,偶尔会考,次数不是很多

以上为极端情况,一般不会这么长

一般用数组存储,数据个位存在数组第0位,最高位存在数组末端

A+BA+B

思想:根据整数的加法,可以得知:
对其中一位进行加法的步骤:将该位上的数组和进位相加,得到的结果取个位作为该位结果,取十位数作为新的进位。

注意:这样的写法条件:是两个对象都是非负数,如果有一方时负数,可以在转换到数组这一步时去掉符号,然后采用高精度减法;
如果两者都是负的,就都去掉负号后用高精度加法,最后再把负数加回去即可。

#include <iostream>
#include <vector>

using namespace std;

vector<int> add(vector<int> &A, vector<int> &B)
{
    vector<int> c;
    int t = 0;//进位
    
    for (int i = 0; i < A.size() || i < B.size(); i++)
    {
        if (i < A.size()) t += A[i];
        if (i < B.size()) t += B[i];
        c.push_back(t % 10);
        t /= 10;
    }
    if (t) c.push_back(1);
    return c;
}

int main()
{
    string a,b;
    vector<int> A,B;
    
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');
    
    auto c = add(A,B);
    
    for(int i = c.size()- 1; i >= 0; i--) printf("%d", c[i]);
    return 0;
}

或者写成这种,只判断一个,当A的位数小于B就计算add(B,A)然后返回

vector<int> add(vector<int> &A, vector<int> &B)
{
    if (A.size() < B.size()) return add(B, A);
    
    vector<int> c;
    int t = 0;//进位
    
    for (int i = 0; i < A.size() ; i++)
    {
        t += A[i];
        if (i < B.size()) t += B[i];
        c.push_back(t % 10);
        t /= 10;
    }
    if (t) c.push_back(1);
    return c;
}

ABA-B

思想:对于某一步,比较被减位和减位,如果不够减,则令被减位的高位减1、被减位加10再进行减法;如果够减,则直接减。
最后一步要注意减法后高位可能有多余的0,要忽略它们,但也要保证结果至少有一位

#include <iostream>
#include <vector>

using namespace std;

bool cmp(vector<int> &A, vector<int> &B)
{
    if (A.size() != B.size()) return A.size() > B.size();
    for (int i = A.size() - 1; i >= 0; i--)
    {
        if (A[i] != B[i]) 
            return A[i] > B[i];
    }
    return true;
}
vector<int> sub(vector<int> &A, vector<int> &B)
{
    vector<int> c;
    for (int i = 0, t = 0; i < A.size(); i++)
    {
        t = A[i] - t;
        if (i < B.size()) t -= B[i];
        c.push_back((t + 10) % 10);
        //合并t>0和<0两种情况,大于0则不需要借位,小于0需要向高位借1即10
        if (t < 0) t = 1;
        else t = 0;
    }
    
    while(c.size() > 1 && c.back() == 0) c.pop_back();
    return c;
}

int main()
{
    string a,b;
    vector<int> A,B;
    
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');
    if (cmp(A,B))
    {
        auto c = sub(A,B);
        for(int i = c.size() - 1; i >= 0; i--) printf("%d", c[i]);
    }
    else
    {
        auto c = sub(B,A);
        printf("-");
        for(int i = c.size() - 1; i >= 0; i--) printf("%d", c[i]);
    }
    
    
    return 0;
}

A×aA \times a

思想:低精度就是可以用基本数据类型存储的数据,例如int型。

为例,这里147视为高精度bign类型,而35视为int类型。并在下面的过程中,始终将35看成一个整体。

image.png

步骤:

  • 1,,取个位数5作为该位结果,高位部分24作为进位;
  • 2,,加上进位24,去各位4作为该位结果,高位进位16作为进位;
  • 3,,加上进位16,得51,取个位数1作为该位结果,高位部分5作为进位;
  • 4, 没得乘了,此时进位还不为0,把进位5直接作为结果的高位。
    最迂某一步来说这每一步结果:取bign的某位与int型整体相乘,在与进位相加,所得结果的个位数作为该位结果,高位部分作为新的进位。
#include <iostream>
#include <vector>

using namespace std;

vector<int> mul(vector<int> &A, int b)
{
    vector<int> c;
    int t = 0;//进位
    
    for (int i = 0; i < A.size() || t; i++)
    {
        if(i < A.size()) t += A[i] * b;
        c.push_back(t % 10);
        t /= 10;
    }
    
    return c;
}

int main()
{
    string a;
    int b;
    vector<int> A;
    
    cin >> a >> b;
    
    for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
    
    auto c = mul(A, b);
    
    for (int i = c.size() - 1; i >= 0; i--) printf("%d", c[i]);
    
    return 0;
}

A÷bA \div b

思想:除法的计算方法与小学的是相同的。
以1234/7为例:

image.png

步骤:

  • 1 1与7比较,不够出,因此该位商0,余数为1;
  • 2 余数1与新位2组合成12,12和7比较,够除,商为1,余数为5;
  • 3 余数5与新位3组合成53,53与7比较,够除,商为7,余数为4;
  • 4 余数4与新位4组合成44,44与7比较,够除,商为6,余数为2。

归纳其中某一步的步骤:上一步的余数乘以10加上该步的位,得到该步临时的被除数,将其与除数比较:如果不够除,则该位的商为0;如果够除,则商即为对应的商,余数即为对应的余数。最后一步要注意减法后高位可能有多余的0,要忽略它,但也要保证结果至少有一位。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> div(vector<int> &A, int b, int &r)
{
    vector<int> c;
    
    r = 0;
    for (int i = A.size() - 1; i >= 0; i--)
    {
        r = r * 10 + A[i];
        c.push_back(r / b);
        r %= b;
    }
    
    reverse(c.begin(), c.end());
    
    while(c.size() > 1 && c.back() == 0) c.pop_back();
    
    return c;
}

int main()
{
    string a;
    int b;
    
    cin >> a >> b;
    
    vector<int> A;
    
    for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
    
    int r;
    auto c = div(A, b, r);
    
    for (int i = c.size() - 1; i >= 0; i--) printf("%d", c[i]);
    
    cout << endl << r << endl;
    return 0;
}