C 计算长双倍于叮当声–;编译器错误?
这是叮当作响的虫子吗 这将打印出最大双倍值:C 计算长双倍于叮当声–;编译器错误?,c,clang,C,Clang,这是叮当作响的虫子吗 这将打印出最大双倍值: long double a = DBL_MAX; printf("%Lf\n", a); long double a = LDBL_MAX; printf("%Lf\n", a); 它是: /* … bigger, but not displayed here. For a good reason. ;-) */ 179769313486231570814527423717043567908070705655675258444996598917
long double a = DBL_MAX;
printf("%Lf\n", a);
long double a = LDBL_MAX;
printf("%Lf\n", a);
它是:
/* … bigger, but not displayed here. For a good reason. ;-) */
17976931348623157081452742371704356790807070565567525844499659891747680315726078089563276687817154045895351438246423432132688946418227684675467035516986049910576551282076245490038932894407585845513394583236903222981658085332334827479262626244723168738177180919299888840402848368.000000
这将打印出最大长双精度值:
long double a = DBL_MAX;
printf("%Lf\n", a);
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
这是预期的行为
long double a = 1.L + DBL_MAX + 1.L;
long double
类型是浮点型:它具有有限的精度。大多数运算的结果四舍五入到最接近的可表示值
请参阅。不是一个bug
0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
因此,1.L+DBL_MAX
的精确数学结果为:
0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
。。。但这不能表示为长双精度
,因此计算结果被四舍五入到最接近的可表示的长双精度
,即DBL_MAX
;添加1
不会(也不应该)更改该值
(它向下舍入而不是向上舍入,因为下一个较大的可表示数字是
0xfffffffffffff801000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
这比DBL_MAX更远离数学上精确的结果。IEE754浮点
double
的尾数为53位宽(52个物理+1个隐式位)。这意味着double
可以准确地表示-2^53…+2^53
范围内的连续整数(即从-9007199254740992
到+9007199254740992
)。之后,该类型就不能再精确地表示连续整数了。相反,该类型只能表示偶数整数值。根据一些特定于实现的规则,任何奇数值都将四舍五入到相邻的偶数值。因此,在double
中向9007199254740992
添加1
可能会由于舍入而导致结果为零。从该限制开始,您必须添加至少2
,以查看值的变化(直到您达到添加2
将停止任何效果的点,并且您必须添加至少4
,依此类推)
如果平台上的长双精度大于双精度,则相同的逻辑适用于长双精度。在x86上,long double可能指的是硬件80位浮点类型和64位尾数。这意味着即使使用这种类型,连续整数的精确表示范围也仅限于-2^64…+2^64
DBL_MAX
的值是far,far,FAAAAR超出该范围。这意味着尝试将1
添加到DBL_MAX
不会对该值产生任何影响。添加2
也不会有任何效果。既不会4
,也不会1024
,甚至也不会4294967296
。您必须在2^960
区域(实际上是nextafter(2^959)
)中添加一些内容,以便对以80位长双精度格式存储的DBL\u MAX
值产生影响。这是一个技术上不太正确的答案,希望能帮助:
数字由符号、指数和分数表示
在本页中,提供了有关C数据类型的信息()。图表声称长双精度数据不能保证是比双精度数据“更大”的数据类型;但是,由于C99,如果目标体系结构上存在,则可以保证这一点(附录F IEC 60559浮点算法)。来自DBL_MAX
和LDBL_MAX
的结果表明,在您的实现中,它实际上使用了更多的位
下面是正在发生的事情:
您有以下格式的数字:
如果是双倍的话
在long中,有一个80位表示()
您可以将double类型放入长double类型中,这样就不会产生任何问题。但是,请注意,小数点是“浮动的”(因此得名),并不是数字中的所有数字都表示出来。计算机表示最重要的数字,然后是和指数(因此这就像我写1234567 E 234,例如,请注意,我没有写该数字的所有234位)。当您尝试向其中添加1时,将不表示1所在位置的数字(由于指数的大小),因此四舍五入后将忽略该数字
有关更多详细信息,请在此处阅读浮点()如果long double in clang只有64位,为什么LDBL_MAX大于DBL_MAX?@AndrewW:64位精度;它是80位类型。(而double是一种64位类型,精度为53位)。在前16位左右之后,其余数字没有意义;double
类型根本不存储任何数字。是的,是的,是的,我很笨。当然,我知道,但我不记得了+非常感谢。我只是忘了+1.