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

似乎 gcc printflong double 截断为 13 位 +-。

解决方法

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

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

更精确

所以它没有任何问题。在 x86 上,long double 通常是 80-bit extended precision 类型,其精度为 around 18 digits3.141592653589793238 正确到 19 位有效数字

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

Demo on Godbolt

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


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

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

在 80 位扩展格式中,内部表示是 10 个字节,但添加了填充字节以将类型对齐到 4 个字节 (x86) 或 8 个字节 (x86-64),从而在 64-位模式。可以通过 gcc

中的 -m96/128bit-long-double 选项更改填充

此处重要的是 LDBL_DIG/LDBL_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 中,您还可以看到 the same phenomena when changing the floating-point format。虽然大小仍然是 16 字节,但精度可能会有所不同。使用 -mabi=ibmlongdoubledouble-double arithmetic,这是默认值)你会得到 (size,digits10,digits2) = (16,31,106) 但是使用 -mabi=ieeelongdouble 元组将变成 (16,33,113)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...