第15位后的十进制精度丢失 - 错误的PI

时间:2014-06-30 08:26:20

标签: c math double-precision

尝试打印超过15个十进制数字PI会导致在十五分之一后打印错误的小数位数。尽管分配了30个正确的十进制值,尽管使用long double来保存该值。以下测试用例清楚地显示了错误。

这是出乎意料的。如果数字中有任何错误,我不希望在耗尽IEEE-754有效数字后第25位之前看到任何错误。在这里可以解释的规则是什么,我可以打印回我刚刚分配给下面的sPI的相同30位数字。这也会影响打印M_PI中包含的math.h表示的能力。

#include <stdio.h>

int
main (void) {

    // static PI approximation (glibc man 1.17)
    long double sPI = 3.14159265358979323846264338327;
    char strPI[]   = "3.14159265358979323846264338327";

    printf ("\n %s (strPI - string - correct)\n", strPI);
    printf (" %.29Lf (sPI - long double - INCORRECT)\n\n", sPI);

    return (0);
}

输出:

3.14159265358979323846264338327 (strPI - string - correct)
3.14159265358979311599796346854 (sPI - long double - INCORRECT)
                 ^^^^^^^^^^^^^^

据推测,此十进制错误适用于任何小数超过16的十进制数。当作为字符串打印时,PI打印正常(显然),但是当打印为双精度时 - 小数精度在小数点后15位分解。造成这种情况的原因是什么?

非常有趣,因为建议在浮点文字的末尾添加L确实有帮助:

 3.14159265358979323846264338327 (strPI - string - correct)
 3.14159265358979323851280895941 (sPI - long double - INCORRECT)

提供了3个额外的精度小数。为清楚起见,这是在旧的AMD Phenom X4 9850上的Linux 3.14.1内核,gcc 4.8.2上运行。(AMD基于Turion的笔记本电脑和英特尔P4给出相同的结果)

尝试分配给quadmath.h的{​​{1}}和__float128类型,结果与长双倍相同。还有一些数字可用,但精度仍然在第19位数字上破坏:

sPI

2 个答案:

答案 0 :(得分:5)

您没有将'long double'值存储到变量中,而是默认double。编译器读取浮点值,将其存储为默认类型,然后仅将其转换为 long double“。将其值与“正常”分配的double进行比较时,您可以看到这一点。

要提示编译器将常量存储为long double,请在浮点常量的末尾添加修饰符后缀Ll

示例:

#include <stdio.h>

int main (void)
{
    // static PI approximation (glibc man 1.17)
    double sPI_d     = 3.14159265358979323846264338327;
    long double sPI  = 3.14159265358979323846264338327;
    long double sPI_L= 3.14159265358979323846264338327L;
    char strPI[]     ="3.14159265358979323846264338327";

    printf ("\n %s (strPI - string - correct)\n", strPI);
    printf (" %.29f (sPI - double)\n", sPI_d);
    printf (" %.29Lf (sPI - long double - INCORRECT)\n", sPI);
    printf (" %.29Lf (sPI - long double - BETTER)\n", sPI_L);

    return 0;
}

输出:

 3.14159265358979323846264338327 (strPI - string - correct)
 3.14159265358979311599796346854 (sPI - double)
 3.14159265358979311599796346854 (sPI - long double - INCORRECT)
 3.14159265358979323851280895941 (sPI - long double - BETTER)

另请参阅What is the precision of long double in C++? - 对于long double,您不能指望超过18位有效数字。

答案 1 :(得分:3)

双精度浮点值表示为64位值,有限精度为15-16位十进制数。因此,long double似乎在您的系统上实现为64位双精度。

如果您不熟悉这些问题,我强烈推荐David Goldberg的永恒文章What Every Computer Scientist Should Know About Floating-Point Arithmetic