📅  最后修改于: 2023-12-03 14:56:45.421000             🧑  作者: Mango
C++中的数值类型有限制精度,可能会在计算过程中出现精度丢失的问题。如果需要精确的数学运算,例如金融或科学计算,我们需要使用高精度运算。这篇文章将介绍C++中的一些常用高精度运算库及其实现方法。
高精度数值是由一串数字来表示的。C++中常使用的方法是将数字保存在一个字符数组中。每位数字使用一个字符来表示,例如字符数组ch[] = {'1', '2', '3', '4'}
等价于数字1234
。为了方便操作,数组的首位默认为最高位,即数组末尾为个位。
在代码实现过程中,我们需要对字符数组进行加、减、乘、除等数学运算。具体步骤如下:
高精度加法需要从低位开始逐位相加,同时需要注意进位问题。例子:
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。
高精度减法需要从低位开始逐位相减,同时需要注意借位问题。例子:
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。
高精度乘法需要将每位数与另一数的每一位相乘,然后将积累加起来。例子:
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。
高精度除法需要从高位开始逐位除以除数,在不足除数的情况下需要添加前导零,商也需要用字符数组保存。例子:
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++高精度运算库:
GMP(GNU Multiple Precision Arithmetic Library),是一个用来实现任意精度算术运算的库。它提供了一系列高精度运算函数来进行加、减、乘、除、余数、幂等算术运算,并且能够处理有理数、浮点数、矩阵等类型。GMP基于GNU通用公共许可证(LGPL)发行。
MPIR(Multiple Precision Integers and Rationals)是一个高精度运算库,类似GMP。它提供了大整型、有理数、浮点数(高精度浮点)等各种数据类型的支持,并提供了一系列高精度运算函数。
NTT(Number Theoretical Transform)是一个基于NTT算法的高精度运算库。NTT算法是一个高效的多项式乘法算法,通过它可以实现高效的大数乘法。NTT提供了高精度的加、减、乘、除、余数、幂等运算,并支持大数比较和位运算。