float a = 0; while (true) { a++; if (a > 16777216) break; // Will never break... a stops at 16777216 }
谁能向我解释一下为什么此代码中的浮点值在16777216处停止递增?
编辑:
甚至更简单:
float a = 16777217; // a becomes 16777216
简短总结一下IEEE-754浮点数(32位):
(+1) * 2^24 * (1.0 + 2^-24) = 2^24 + 1 = 16777217
1.5625
现在来看您的示例:
16777216就是224,将被表示为32位浮点数,如下所示:
现在让我们看一下数字16777217,或者确切地说是224 + 1:
16777217不能完全用浮点数表示。 浮点数可以准确表示的下一个最高数字是16777218。
因此,您尝试将浮点值16777216增加到16777217,这不能用浮点表示。
当以二进制表示形式查看该值时,您会看到它是一个零,即1 0000 0000 0000 0000 0000 0000,或恰好是2 ^ 24。 这意味着,在16777216,这个数字刚刚增长了一位数字。
1 0000 0000 0000 0000 0000 0000
由于它是一个浮点数,因此这可能意味着其末尾仍要存储的最后一个数字(即在其精度范围内)也向左移动。
可能您看到的是精度的最后一位刚移到大于1的位置,因此加1不再有任何区别。
想象一下十进制形式。 假设您有电话号码:
1.000000 * 10^6
或1,000,000。 如果您所获得的准确度只有六位数,则在此数字上加上0.5将产生
1.0000005 * 10^6
但是,当前使用fp舍入模式的想法是使用“四舍五入”,而不是“四舍五入”。 在这种情况下,每次增加此值时,它将以浮点数单位四舍五入到16,777,216或2 ^ 24。 IEE 754中的单打表示为:
+/- exponent (1.) fraction
其中的“ 1”。 在这种情况下,则隐含小数位,并且小数是另外23位,全为零。 多余的二进制数1将溢出到保护位中,进入舍入步骤,并且每次将其删除(无论您递增多少次)。 最后一位的ulp或单位始终为零。 最后一个成功的增量来自:
ulp
+2^23 * (+1.) 11111111111111111111111 -> +2^24 * (1.) 00000000000000000000000