这是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
答案 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
也不会产生任何影响。 4
,1024
,甚至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_MAX
和LDBL_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)