C++ GNU MP Bignum库的数值问题

C++ GNU MP Bignum库的数值问题,c++,gnu,gmp,C++,Gnu,Gmp,我在做一些数字运算,这需要高精度的算术运算。我正在使用GNU MP库,并且: 浮点数,简称浮点数,是具有有限精度指数的任意精度尾数 虽然尾数应该具有任意精度,但我仍然遇到精度问题。这里有一个几乎最小的工作示例来说明我的问题,而不是让您对我的实际代码感到厌烦。代码计算9.3^15、9.8^15和(9.3*9.8)^15。在我的机器上,(9.3^15)*(9.8^15)和(9.3*9.8)^15的值开始从第16位开始不同,在这种情况下,会导致(大约)4.94*10^13的错误 任何帮助都将不胜感激。

我在做一些数字运算,这需要高精度的算术运算。我正在使用GNU MP库,并且:

浮点数,简称浮点数,是具有有限精度指数的任意精度尾数

虽然尾数应该具有任意精度,但我仍然遇到精度问题。这里有一个几乎最小的工作示例来说明我的问题,而不是让您对我的实际代码感到厌烦。代码计算9.3^15、9.8^15和(9.3*9.8)^15。在我的机器上,(9.3^15)*(9.8^15)和(9.3*9.8)^15的值开始从第16位开始不同,在这种情况下,会导致(大约)4.94*10^13的错误

任何帮助都将不胜感激。代码如下

#include <gmp.h>
#include <gmpxx.h>

#include <iostream>
#include <iomanip>

int main()
{
    mpf_class x, y, z;
    x = y = z = 1.0;

    for (int i = 0; i < 15; i++)
    {
        x *= 9.3;
        y *= 9.8;
        z *= 9.3*9.8;
    }

    std::cout << z - x*y << std::endl;

    return 0;
}
#包括
#包括
#包括
#包括
int main()
{
强积金(x,y,z级);;
x=y=z=1.0;
对于(int i=0;i<15;i++)
{
x*=9.3;
y*=9.8;
z*=9.3*9.8;
}

std::cout您看到的问题是由于9.3*9.8的计算结果约为。请将文本更改为mpf_类的实例:

mpf_class a, b;
a = 9.3;
b = 9.8;

// ...

x *= a;
y *= b;
z *= a * b;

如果您需要<强>无限>强>精度,请考虑使用<强>有理数< /强>代替:

#include <gmp.h>
#include <gmpxx.h>

#include <iostream>
#include <iomanip>

int main()
{
    mpq_class x(1), y(1), z(1), a(93, 10), b(98, 10);

    for (int i = 0; i < 15; i++)
    {
        x *= a;
        y *= b;
        z *= (a * b);
    }

    std::cout << z - x*y << std::endl << z << std::endl;

    return 0;
}

问题是你没有明确设置精度,所以你得到默认精度,通常是(我认为)64位,因此,由于不同计算方式的不同舍入,最后一位的结果不同。这使得大约20位成为通用前缀(随着计算的增加,差异可能会变得更大).如果设置更高的精度

#include <gmp.h>
#include <gmpxx.h>

#include <iostream>
#include <iomanip>

int main()
{
    mpf_class x(1.0,200), y(1.0,200), z(1.0,200), a("9.3",200), b("9.8",200), c(0,200); 
    c = a*b;

    for (int i = 0; i < 15; i++)
    {
        x *= a;
        y *= b;
        z *= c;
    }

    std::cout << z << "\n" << (x*y) << std::endl;
    std::cout << z - x*y << std::endl;

    return 0;
}
因此,一个大约80位十进制数字的公共前缀,或接近256位(64位的最小倍数大于199位)


精度为2000时,字符串构造函数的差值为-2.78942e-588,如果从
double
初始化,则为0(当然,初始精度限制为53位,因此仅意味着两种方式以相同的方式累积错误).

9.3
9.8
不能用二进制精确表示。这似乎只是15的幂次幂,而不是100。你说得对。我用了100的幂次幂,但后来意识到15足以说明我的观点。编辑。@Mystical:谢谢你的评论。老实说,我不明白你为什么这么说,但如果这是有区别的,93和98的问题仍然存在。这些只是整数-当然它们可以精确表示。因为
9.3
9.8
不能用
double
表示,当它们被精度扩展到您想要的精度时,它们将保留
+/-10^-16
错误。这就是GMP主要问题。如果它不能处理偶数整数,那么还有一个单独的问题。我怀疑你没有正确使用库,但我没有使用GMP,所以我说不出来。我没有注意到,但我明白你的意思-9.3和9.8是双倍的,而不是
mpf\u类
@Oleg2718281828你的意思是定义:mpf\u类*x=新的mpf\u类;?@Oleg2718281828感谢您的贡献。它确实起到了作用-误差减少到10^9的数量级。但我真的需要它为零。这是因为它仍然使用
9.3
9.8
。请记住,它们被视为
double
,因此在转换为
mpf\u类之前,它们会相应地四舍五入e> @mga我想你需要的是有理数而不是浮点。我相信
a=9.3;
仍然需要更改。也许它会接受
a=“9.3”
。尽管我在文档中找不到赋值运算符重载的列表。即使我有
a(9.3200),b(9.8200)
在声明中,输出保持不变。在构造函数中使用字符串实际上更糟糕。嗯……缺少关于GMP精度行为的文档肯定没有帮助。有一件事是可以肯定的,那就是你不能使用浮点文本,除非它们是完全可表示的。啊,糟了,我忘了设置
c
的精度:(仔细想想,这个特定的示例之所以通过,是因为它使用的是相同的(由于
double
舍入而不正确)这两种计算的
9.3
9.8
值。如果将结果与不同的库进行比较,它们仍然只有大约16位精度。如果GMP不接受字符串作为参数,则您几乎必须以全精度手动进行10除法。
#include <gmp.h>
#include <gmpxx.h>

#include <iostream>
#include <iomanip>

int main()
{
    mpf_class x(1.0,200), y(1.0,200), z(1.0,200), a("9.3",200), b("9.8",200), c(0,200); 
    c = a*b;

    for (int i = 0; i < 15; i++)
    {
        x *= a;
        y *= b;
        z *= c;
    }

    std::cout << z << "\n" << (x*y) << std::endl;
    std::cout << z - x*y << std::endl;

    return 0;
}
$ ./a.out 
2.48677e+29
2.48677e+29
-4.80637e-49