GNU MP 使用 mpf_pow 函数时精度低

GNU MP low precision while using mpf_pow function

在编写 WolframAlpha 给出的 , I used the mpf_pow function to calculate 12.3 ^ 123, and the result is different from the one 时(顺便说一句,它也使用 GMP)。

我将代码转换为纯 C 以简化:

#include <stdio.h>
#include <gmp.h>

int main (void) {
    mpf_t a, c;
    unsigned long int b = 123UL;
    mpf_set_default_prec(100000);
    mpf_inits(a, c, NULL);
    mpf_set_d(a, 12.3);
    mpf_pow_ui(c, a, b);
    gmp_printf("c = %.50Ff\n", c);
    return 0;
}

结果是

114374367934618002778643226182707594198913258409535335775583252201365538178632825702225459029661601216944929436371688246107986574246790.32099077871758646985223686110515186972735931183764

虽然 WolframAlpha returns

1.14374367934617190099880295228066276746218078451850229775887975052369504785666896446606568365201542169649974727730628842345343196581134895919942820874449837212099476648958359023796078549041949007807220625356526926729664064846685758382803707100766740220839267 × 10^134

在第 15 位开始与 mpf_pow 不一致。

我是不是在代码中做错了什么,这是 GMP 的限制,还是 WolframAlpha 给出了错误的结果?

这给出了类似于 WolframAlpha 的结果:

from decimal import Decimal
from decimal import getcontext

getcontext().prec = 200

print(Decimal('12.3') ** 123)

所以你的 GMP 配置一定有问题。

Am I doing something wrong in the code, is this a limitation of GMP, or is WolframAlpha giving an incorrect result?

您正在做的事情与 Wolfram 正在做的事情不同(很明显)。您的代码没有错,本身,但它并没有按照您可能认为的那样做。比较此变体的输出:

#include <stdio.h>
#include <gmp.h>

int main (void) {
    mpf_t a, c;
    unsigned long int b = 123UL;
    mpf_set_default_prec(100000);
    mpf_inits(a, c, NULL);
    mpf_set_d(a, 12.3);
    mpf_pow_ui(c, a, b);
    gmp_printf("c  = %.50Ff\n", c);

    putchar('\n');
    mpf_t a1, c1;
    mpf_inits(a1, c1, NULL);
    mpf_set_str(a1, "12.3", 10);
    mpf_pow_ui(c1, a1, b);
    gmp_printf("c' = %.50Ff\n", c1);

    return 0;
}

...

c  = 114374367934618002778643226182707594198913258409535335775583252201365538178632825702225459029661601216944929436371688246107986574246790.32099077871758646985223686110515186972735931183764

c' = 114374367934617190099880295228066276746218078451850229775887975052369504785666896446606568365201542169649974727730628842345343196581134.89591994282087444983721209947664895835902379607855

两个输出值之间的差异是因为我的 C 实现和你的实现以二进制浮点数表示 double 类型的值,而 12.3 在二进制浮点数中不能完全表示(参见 Is floating point math broken?)。 C 提供了最接近的可用近似值,假设使用 64 位 IEEE 754 表示,它与大约 15 位十进制数字的精度相匹配。当您使用这样的值初始化 GMP 变量时,您将获得实际 double 值的精确 GMP 表示,这只是十进制 12.3 的近似值。

但是 GMP 可以将 12.3(十进制)表示为您选择的任何精度。*您选择了非常高的精度,因此当您使用十进制字符串初始化 MP-float 时与使用 double 相比,您可以获得更接近的近似值。当然,对这些不同的值执行相同的操作会产生不同的结果。后一种情况下的 GMP 结果似乎与 Wolfram 结果一致,完全精确地表达了它。

另请注意,在一般意义上,也可以在软件或硬件(如果您具备这种能力)中使用十进制浮点数。值 12.3(十进制)可以用这种格式准确表示,但这不是 GMP 使用的格式。


* 或者实际上,GMP 可以将 12.3 准确地 表示为 MP 有理数,尽管上面的代码不是这样做的。