首页 > 解决方案 > gcc 9.3.0 printf %Lf 输出不正确

问题描述

printf("%Lf", long double)将输出截断为double. 或者,我做错了什么?

Gcc 9.3.0 Ubuntu 20.04.2,Linux 5.8.0-59-generic

printf("print m_pi  %.45f\n", M_PI);
printf("print m_pil %.45LF\n", M_PIl);
printf("maple Pi    3.141592653589793238462643383279502884197\n");
printf("m_pil def   3.141592653589793238462643383279502884L\n");

输出:

print m_pi  3.141592653589793115997963468544185161590576172
print m_pil 3.141592653589793238512808959406186204432742670
maple Pi    3.141592653589793238462643383279502884197
m_pil def   3.141592653589793238462643383279502884L

似乎 gccprintf正在截断long double为 13 位数字 +-。

标签: gccfloating-pointprintfprecisionlong-double

解决方案


printf("%Lf", long double)将输出截断为两倍

它显然不会截断任何内容。很容易看到结果,double并且long double完全不同,并且long doubledouble

所以这没有什么问题。在 x86long double上通常是80 位扩展精度类型,其精度约为 18 位,并且3.141592653589793238对 19 位有效数字是正确的

如果您需要更高的精度,则通过设置选项或直接使用来使用四倍精度。当然这是由软件模拟的,因此它会比原生 x87 80 位格式慢得多-mlong-double-128__float128

在 Godbolt 上演示

要获得更高的精度,您必须使用 3 rd方任意精度浮点库


为什么是size(long double)16 字节?

尺寸与精度无关。就像 C++ 中允许的陷阱表示和填充位一样,类型的大小可以比实际精度更宽

在 80 位扩展格式中,内部表示为 10 字节,但添加了填充字节以将类型对齐为 4 字节 (x86) 或 8 字节 (x86-64),从而在 64 位模式下产生 16 字节类型。可以通过-m96/128bit-long-doublegcc 中的选项更改填充

这里重要的是///或者LDBL_DIGLDBL_MANT_DIG类型的真正精度LDBL_MAX_EXP。事实上,通过改变上述选项的组合,你会得到许多不同的结果:LDBL_MAX_10_EXPstd::numeric_limits<long double>::digits/digits10/max_exponent/max_exponent10-m-long-double

默认结果是 size = 16, digits10 = 18, digits2 = 64。如果禁用填充,您将得到 size = 10,但由于未对齐,这会以性能为代价。在Godbolt上查看更多演示

在 PowerPC 中,您也可以在更改浮点格式时看到相同的现象。虽然大小仍然是 16 字节,但精度可能会有所不同。使用-mabi=ibmlongdouble双双算术,这是默认值)您将得到 (size, digits10, digits2) = (16, 31, 106) 但-mabi=ieeelongdouble元组将变为 (16, 33, 113)


推荐阅读