在clang中使用long double进行计算 - 编译器错误?

时间:2013-06-27 16:08:18

标签: c clang

这是clang中的错误吗?

这将打印出最大双倍值:

long double a = DBL_MAX;
printf("%Lf\n", a);

是:

  

179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000

这会打印出最大的长双倍值:

long double a = LDBL_MAX;
printf("%Lf\n", a);

是:

/* … bigger, but not displayed here. For a good reason. ;-) */

这很清楚。

但是当我使用算术表达式,即可编译为初始化程序的编译时,我得到了一个令人惊讶的结果:

long double a = 1.L + DBL_MAX + 1.L;
printf("%Lf\n", a); 

这仍然打印出DBL_MAX而不是DBL_MAX + 2!?

如果计算在运行时完成,则相同:

long double b = 2.L;
long double a = DBL_MAX;
printf("%Lf\n", a+b);

仍为DBL_MAX。

$ clang --version
Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin12.4.0
Thread model: posix

4 个答案:

答案 0 :(得分:7)

不是错误。 clang / x86_64中的long double具有64位精度,并且舍入结果以适合该格式。

如果我们使用十六进制而不是二进制,这将更加清晰。 DBL_MAX是:

0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

1.L + DBL_MAX的确切数学结果是:

0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001

...但是这不能表示为long double,因此计算结果四舍五入到最接近的可表示long double,这只是DBL_MAX;添加1不会(也不应该)更改该值。

(它向下舍入而不是向上,因为下一个更大的可表示数字是

0xfffffffffffff801000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

很多远离数学上精确的结果,而不是DBL_MAX。)

答案 1 :(得分:7)

IEE754浮点double的尾数为53位宽(52个物理+ 1个隐含位)。这意味着double可以准确地表示-2^53...+2^53范围内的连续整数(即从-9007199254740992+9007199254740992)。之后,类型不能再精确地表示连续的整数。相反,该类型只能表示偶数整数值。根据一些特定于实现的规则,任何奇数值将四舍五入为相邻的偶数值。因此,完全可以预期在1内向9007199254740992添加double可能会因舍入而导致任何结果。从该限制开始,您必须至少添加2才能看到值的变化(直到您达到添加2将不再产生任何影响的程度,您将不得不至少添加4,依此类推。)

同样的逻辑适用于long double,如果它大于您平台上的double。在x86上long double可能指的是具有64位尾数的硬件80位浮点类型。这意味着即使使用该类型,您精确表示连续整数的范围也仅限于-2^64...+2^64

DBL_MAX的值远远超出FAR, FAAAAR!。这意味着尝试将1添加到DBL_MAX不会对该值产生任何影响。添加2也不会产生任何影响。 41024,甚至4294967296也不会。您必须在2^960区域(实际为nextafter(2^959))中添加内容,以便对以80位DBL_MAX格式存储的long double值产生影响。

答案 2 :(得分:4)

这是预期的行为。

long double a = 1.L + DBL_MAX + 1.L;

long double类型是浮点数:它具有有限的精度。大多数操作的结果四舍五入到最接近的可表示值。

请参阅What Every Programmer Should Know About Floating-Point Arithmetic

答案 3 :(得分:2)

一个技术上不正确的答案,希望有所帮助:

数字由符号,指数和分数表示。

在此页面上,提供了有关C数据类型的信息(https://en.wikipedia.org/wiki/C_data_types)。该图表声称,long double不能保证是“大”数据类型而不是double;但是,如果C99存在于目标体系结构上(附件F IEC 60559浮点运算),则可以保证这一点。来自DBL_MAXLDBL_MAX的结果显示,在您的实现中,它确实使用了更多位。

所以这就是发生的事情:

您有以下格式的数字:

是双重的

< 1 bit>< 11 bits>< 52 bits>

总之,你有这个80位表示(https://en.wikipedia.org/wiki/Extended_precision

< 1 bit>< 15 bits>< 64 bits>

您可以将double类型放入long double类型中,这样就不会出现问题。但是,请注意小数点是“浮动”(因此名称),并不表示数字中的所有数字。计算机代表最重要的数字,然后是指数(所以就像我写1234567 E 234一样,注意我没有写出那个数字的所有234位数)。当你试图向它添加1时,一个位置的数字没有被表示(由于指数的大小),所以在四舍五入后这将被忽略。

有关详细信息,请在此处阅读浮点(https://en.wikipedia.org/wiki/Double_precision_floating-point_format