4

我正在做一些数字运算,这需要高精度算术。我正在使用 GNU MP 库,并根据 GMP 手册

“浮点数或简称Float,是具有有限精度指数的任意精度尾数。”

尽管尾数应该具有任意精度,但我仍然遇到精度问题。与其用我的实际代码让你厌烦,不如用一个近乎最小的工作示例来说明我的问题。代码计算 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;
}
4

2 回答 2

2

您看到的问题是由于 9.3 * 9.8 是近似计算的。请将文字更改为 mpf_class 的实例:

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;
}

印刷

0
7589015305950762920038660273144124106674963183136666693/30517578125000000000000000
于 2012-07-22T22:17:59.563 回答
1

问题是您没有明确设置精度,因此您获得了默认精度,通常(我认为)是 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;
}

在这里 200 位,你会得到更准确的结果:

$ ./a.out 
2.48677e+29
2.48677e+29
-4.80637e-49

所以一个大约 80 个十进制数字的公共前缀,或接近 256 位(64 的最小倍数大于 199)。

精度为 2000 时,与字符串构造函数的差异为 -2.78942e-588,如果从以下位置初始化,则为 0 double(但当然,初始精确度限制为 53 位,因此这仅意味着两种方式都会在一样的方法)。

于 2012-07-22T22:34:27.740 回答