【发布时间】:2014-06-30 08:26:20
【问题描述】:
尝试打印超过 15 个小数位的 PI 会导致小数点后 15 位打印不正确。尽管分配了 30 个正确的十进制值,并且尽管使用 long double 来保存该值,但仍然如此。下面的测试用例清楚地显示了错误。
这是出乎意料的。如果数字中有任何错误,我不希望在耗尽 IEEE-754 有效位后的第 25 位之前看到任何错误。这里有什么规则可以解释我无法打印我刚刚分配给下面 sPI 的 30 位数字。这也会影响打印math.h 中包含的M_PI 表示的能力。
#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 的笔记本电脑和 Intel P4 给出相同的结果)
尝试将quadmath.h 和__float128 类型分配给sPI,结果与long double 相同。还有几个数字可用,但精度仍然在第 19 位出现故障:
3.14159265358979323846264338327 (strPI - string - correct)
3.1415926535897932385128089594061862 (sPI - long double - INCORRECT)
【问题讨论】:
-
看起来
long double仍然是 64 位双精度。并非所有编译器都支持 80 位扩展精度,支持的编译器可能需要编译器开关。 -
你还没有说你使用的是什么编译器。许多编译平台提供 80 位
long double(Linux/x86 上的 GCC,Linux/x86 上的 Clang,……),但更多的编译平台将long double定义为与double相同的类型(Visual Studio)。注意:即使long double是 80 位扩展精度浮点格式,你也不会得到 30 个 π 小数位数,更像是 19-21。 -
您意识到将
double或long double常量分配给任何更精确的东西不会让您满意,对吧?使用l足以满足long double并从字符串中解析常量,如果您要过去long double。
标签: c math double-precision