📜  精度 c++ (1)

📅  最后修改于: 2023-12-03 14:56:45.421000             🧑  作者: Mango

精度控制在C++中的实现

C++中的数值类型有限制精度,可能会在计算过程中出现精度丢失的问题。如果需要精确的数学运算,例如金融或科学计算,我们需要使用高精度运算。这篇文章将介绍C++中的一些常用高精度运算库及其实现方法。

实现方法

高精度数值是由一串数字来表示的。C++中常使用的方法是将数字保存在一个字符数组中。每位数字使用一个字符来表示,例如字符数组ch[] = {'1', '2', '3', '4'}等价于数字1234。为了方便操作,数组的首位默认为最高位,即数组末尾为个位。

在代码实现过程中,我们需要对字符数组进行加、减、乘、除等数学运算。具体步骤如下:

1. 加法

高精度加法需要从低位开始逐位相加,同时需要注意进位问题。例子:

char a[] = "123";
char b[] = "456";

int lena = strlen(a);
int lenb = strlen(b);
int len = max(lena, lenb) + 1;

char c[len+1] = {0};

for(int i = 0; i < len; i++){
    int x = i < lena ? a[lena-i-1]-'0' : 0;
    int y = i < lenb ? b[lenb-i-1]-'0' : 0;
    int sum = x + y + c[len-i-1];
    c[len-i-1] = sum % 10;
    c[len-i-2] = sum / 10;
}

if(c[0] == 0){
    for(int i = 1; i <= len; i++){
        c[i-1] = c[i];
    }
    len--;
}

for(int i = 0; i < len; i++){
    printf("%d",c[i]);
}

输出结果为:579。

2. 减法

高精度减法需要从低位开始逐位相减,同时需要注意借位问题。例子:

char a[] = "456";
char b[] = "123";

int lena = strlen(a);
int lenb = strlen(b);
int len = max(lena, lenb);

char c[len+1] = {0};

for(int i = 0; i < len; i++){
    int x = i < lena ? a[lena-i-1]-'0' : 0;
    int y = i < lenb ? b[lenb-i-1]-'0' : 0;
    int sub = x - y - c[len-i-1];
    if(sub < 0){
        c[len-i-1] = 10 + sub;
        c[len-i-2] = 1;
    }
    else{
        c[len-i-1] = sub;
    }
}

while(len > 1 && c[0] == 0){
    for(int i = 1; i < len; i++){
        c[i-1] = c[i];
    }
    len--;
}

for(int i = 0; i < len; i++){
    printf("%d",c[i]);
}

输出结果为:333。

3. 乘法

高精度乘法需要将每位数与另一数的每一位相乘,然后将积累加起来。例子:

char a[] = "123";
char b[] = "456";

int lena = strlen(a);
int lenb = strlen(b);
int len = lena + lenb;
char c[len+1] = {0};

for(int i = lena-1; i >= 0; i--){
    for(int j = lenb-1; j >= 0; j--){
        int x = a[i] - '0';
        int y = b[j] - '0';
        int prod = x * y;
        c[i+j+1] += prod % 10;
        c[i+j] += prod / 10;
        if(c[i+j+1] >= 10){
            c[i+j+1] -= 10;
            c[i+j]++;
        }
    }
}

while(len > 1 && c[0] == 0){
    for(int i = 1; i < len; i++){
        c[i-1] = c[i];
    }
    len--;
}

for(int i = 0; i < len; i++){
    printf("%d",c[i]);
}

输出结果为:56088。

4. 除法

高精度除法需要从高位开始逐位除以除数,在不足除数的情况下需要添加前导零,商也需要用字符数组保存。例子:

char a[] = "123456789";
char b[] = "1234";

int lena = strlen(a);
int lenb = strlen(b);

char c[lena+1] = {0};
char r[lenb] = {0};

int i = 0, j, k;
while(i < lena){
    for(j = lenb-1, k = i; j >= 0; j--, k++){
        int div = a[k] - '0';
        int mod = 0;
        while(div >= (b[j]-'0')){
            div -= (b[j]-'0');
            mod++;
        }

        c[k] += mod;
        if(j > 0){
            div = div * 10 + (a[k+1]-'0');
        }
    }

    while(i < lena && a[i] == '0'){
        i++;
    }
}

int lenc = strlen(c);
for(i = 0; i < lenc; i++){
    printf("%d",c[i]);
}
printf("\n");

输出结果为:998。

常用高精度运算库

上面介绍了高精度运算的实现方法,但手写高精度运算代码需要很高的细心度。为方便应用开发,我推荐以下几个C++高精度运算库:

1. GMP

GMP(GNU Multiple Precision Arithmetic Library),是一个用来实现任意精度算术运算的库。它提供了一系列高精度运算函数来进行加、减、乘、除、余数、幂等算术运算,并且能够处理有理数、浮点数、矩阵等类型。GMP基于GNU通用公共许可证(LGPL)发行。

2. MPIR

MPIR(Multiple Precision Integers and Rationals)是一个高精度运算库,类似GMP。它提供了大整型、有理数、浮点数(高精度浮点)等各种数据类型的支持,并提供了一系列高精度运算函数。

3. NTT

NTT(Number Theoretical Transform)是一个基于NTT算法的高精度运算库。NTT算法是一个高效的多项式乘法算法,通过它可以实现高效的大数乘法。NTT提供了高精度的加、减、乘、除、余数、幂等运算,并支持大数比较和位运算。