sizeof long double和precision不匹配?

时间:2013-06-29 17:29:17

标签: c floating-point floating-point-precision quad long-double

考虑以下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;
}

gcc 4.8.1下与Ubuntu x64 13.04一起编辑,打印出来:

16 0.123456789012345678901321800735590983

这告诉我一个长的双重16字节,但小数似乎只对第20位。这怎么可能? 16个字节对应四元组,四元组给出33到36个小数。

4 个答案:

答案 0 :(得分:7)

C实现中的long double格式使用Intel格式,带有一位符号,一个15位指数和一个64位有效数字(总共十个字节)。编译器为它分配16个字节,这对于诸如对齐之类的一些事情是浪费的。但是,64位仅提供log 10 (2 64 )有效数字,大约为20位。

答案 1 :(得分:2)

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 width specifier to maintain precision of floating-point value

printf("%.*Le\n", (LDBL_DIG + 3) - 1, ld);
printf("%.*Le\n", LDBL_DECIMAL_DIG - 1, ld);

答案 2 :(得分:1)

您计算机上的格式确实是Intel双扩展精度格式,宽度为80位,具有15位指数和64位尾数。

实际上只有10个连续字节的存储器用于存储。英特尔手册(Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D and 4)说:

  

将浮点值存储在内存中时,半精度值存储在内存中的两个连续字节中;   单精度值存储在内存中的4个连续字节中;双精度值存储在8中   连续字节;和双精度扩展值存储在10个连续字节中。

但是,x86 Linux ABI指定实际使用了完整的16个字节。这可能是因为10字节的值在数组中只能具有 2 的基本对齐要求,这可能会导致特殊的问题。

此外,使用16的倍数进行数组索引更容易。

在大多数情况下,这不是问题,因为long double通常用于使中间计算中的错误最小化,然后将结果截断为double

答案 3 :(得分:0)

sizeof运算符返回数据类型的字节。浮点格式类型实际上与数据类型的字节大小不相上下,其他大小通常意味着更高的精度。