考虑以下C代码:
#include <stdio.h>
int main(int argc, char* argv[])
{
const long double ld = 0.12345678901234567890123456789012345L;
printf("%lu %.36Lf\n", sizeof(ld), ld);
return 0;
}
在Ubuntu x64 13.04
下使用gcc 4.8.1
编译,它打印:
16 0.123456789012345678901321800735590983
这告诉我一个长的双重权重为16个字节,但小数似乎只能到第20位。这怎么可能?16个字节对应一个四边形,一个四边形会给我33到36个小数。
C实现中的long double
格式使用具有一位符号、15位指数和64位有效数(总共10个字节)的Intel格式。编译器为其分配16个字节,这很浪费,但对于对齐等某些事情很有用。但是,64位仅提供log10(264)有效数字,约为20位。
long double
的各种C实现可能具有变体范围和精度。sizeof
暗示了底层浮点符号,但没有指定它。long double
不需要有33到36个小数。它甚至可以具有与double
完全相同的表示。
在不硬编码精度的情况下,但使用所有可用的精度并且不要过度使用,建议:
const long double ld = 0.12345678901234567890123456789012345L;
printf("%.*Le\n", LDBL_DIG + 3, ld);
printf("%.*Le\n", LDBL_DIG + 3, nextafterl(ld, ld*2));
这打印出来(在我的eclipse intel 64位上),当然,你的可能不同。
1.234567890123456789013e-01
1.234567890123456789081e-01
[编辑]
回顾一下,2就足够了。最好使用LDBL_DECIMAL_DIG
。请参阅Printf宽度说明符以保持浮点值的精度
printf("%.*Le\n", (LDBL_DIG + 3) - 1, ld);
printf("%.*Le\n", LDBL_DECIMAL_DIG - 1, ld);
您计算机上的格式确实是Intel双扩展精度格式,80位宽,具有15位指数和64位尾数。
存储器实际仅使用10个连续字节。英特尔手册(英特尔®64和IA-32体系结构软件开发人员手册合并卷: 1、2A、2B、2C、2D、3A、3B、3C、3D和4)如下所述:
在内存中存储浮点值时,半精度值存储在内存中的2个连续字节中;单精度值存储在内存中的4个连续字节中;双精度值存储在8个连续字节中;双扩展精度值存储在10个连续字节中。
但是,x86LinuxABI指定实际使用完整的16个字节。这可能是因为10字节的值在数组中只能有2的基本对齐要求,这可能会导致特殊问题。
此外,使用16的倍数更容易进行数组索引。
大多数情况下,这不是问题,因为long double
通常用于最小化中间计算中的错误,然后将结果截断为double
。