为什么一个浮点variables在C#中停止增加16777216?

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位符号(0表示正数,1表示负数)
  • 8位指数(与-127偏差,这里不重要)
  • 23位“尾数”
  • 除了指数值0和255的例外,您可以计算该值为: (sign ? -1 : +1) * 2^exponent * (1.0 + mantissa)
    • 尾数位表示小数分隔符二进制数字,例如1001 0000 0000 0000 0000 000 = 2^-1 + 2^-4 = .5 + .0625 = .5625并且小数点前面的值未被存储,隐式地假设为1(如果指数是255,则假设为0,但在这里并不重要),例如,对于指数为30的情况,例如,这个尾数的例子代表值1.5625

现在来看你的例子:

16777216正好是2 24 ,并将表示为32位浮点数如下所示:

  • 标志= 0(正数)
  • 指数= 24(存储为24 + 127 = 151 = 10010111
  • 尾数= .0
  • 作为32位浮点表示: 0 10010111 00000000000000000000000
  • 因此:值= (+1) * 2^24 * (1.0 + .0) = 2^24 = 16777216

现在让我们来看一下16777217,或者正好是2 24 +1:

  • 符号和指数是相同的
  • 尾数必须正好是2-24,所以(+1) * 2^24 * (1.0 + 2^-24) = 2^24 + 1 = 16777217
  • 这是问题。 尾数不能有2 – 24的值,因为它只有23位,所以16777217不能用32位浮点数的精度表示!

当你用二进制表示来看这个值时,你会发现它是一个很多的零,也就是1 0000 0000 0000 0000 0000 0000 ,或者正好是2 ^ 24。 这意味着,在16777216,这个数字刚刚增长了一位数字。

因为它是一个浮点数,这可能意味着最后一个仍然存储的数字(即在其精度内)也向左移动。

可能你所看到的是精度的最后一位数字已经转移到大于一的数字,所以加一个没有任何区别。

16777217不能完全用浮点表示。 浮动可以代表的下一个最高数字是16777218。

因此,您尝试将float值16777216增加到16777217,这不能用浮点数表示。

想象一下这个十进制forms。 假设你有这个号码:

 1.000000 * 10^6 

或100万。 如果你所有的数字都是六位数的,那么在这个数字上加0.5即可

 1.0000005 * 10^6 

然而,目前的fp舍入模式的思路是使用“Round to Even”,而不是“Round to Nearest”。 在这种情况下,每次你增加这个值,它将在浮点单元中回到16,777,216或2 ^ 24。 IEE 754中的单打选手performance为:

 +/- exponent (1.) fraction 

那里的“1” 是隐含的,在这种情况下,分数是另外的23位,全是0。 额外的二进制数字1将溢出到警戒位,进行舍入步骤,并且每次删除,无论你增加多less次。 在最后的地方 ulp单位将始终为零。 最后一个成功的增量来自:

 +2^23 * (+1.) 11111111111111111111111 -> +2^24 * (1.) 00000000000000000000000