15位数字后失去小数精度-错误的PI

Dav*_*ica -2 c math double-precision

尝试打印多于15个小数位的PI结果会导致在第15个小数位之后打印不正确的小数。尽管分配了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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

3.14159265358979323846264338327 (strPI - string - correct)
3.14159265358979311599796346854 (sPI - long double - INCORRECT)
                 ^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

大概,此十进制错误将适用于精度大于16的任何十进制数字。当以字符串形式打印时,PI可以正常打印(显然),但是以双精度形式打印时-小数精度会在第15个小数点后下降。是什么原因造成的?

非常有趣,正如建议L在浮点文字的末尾添加一个确实有帮助:

 3.14159265358979323846264338327 (strPI - string - correct)
 3.14159265358979323851280895941 (sPI - long double - INCORRECT)
Run Code Online (Sandbox Code Playgroud)

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

尝试使用quadmath.h__float128分配给的类型sPI,结果与long double相同。还有几个数字可用,但精度在第19个数字上仍然有问题:

3.14159265358979323846264338327 (strPI - string - correct)
3.1415926535897932385128089594061862 (sPI - long double - INCORRECT)
Run Code Online (Sandbox Code Playgroud)

usr*_*301 5

您不是将'long double'值存储到变量中,而是默认值double。编译器读取浮点值,将其存储为默认类型,然后仅将其强制转换为long double。将其值与“正常”分配的进行比较时,您会看到此信息double

要提示编译器将常量存储为a 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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

 3.14159265358979323846264338327 (strPI - string - correct)
 3.14159265358979311599796346854 (sPI - double)
 3.14159265358979311599796346854 (sPI - long double - INCORRECT)
 3.14159265358979323851280895941 (sPI - long double - BETTER)
Run Code Online (Sandbox Code Playgroud)

另请参见C ++中long double的精度是多少?-您不能期望超过18位有效数字long double