为什么浮点变量在C#中停止递增到16777216?

Jan*_*Jan 49 c# floating-point

float a = 0;
while (true)
{
    a++;
    if (a > 16777216)
        break; // Will never break... a stops at 16777216
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以向我解释为什么浮点值在此代码中停止递增16777216?

编辑:

或者更简单:

float a = 16777217; // a becomes 16777216
Run Code Online (Sandbox Code Playgroud)

And*_*Dog 56

我最喜欢的IEEE-754浮点数(32位)的简短综述:

  • 1位符号(0表示正数,1表示负数)
  • 8位指数(-127偏差,这里不重要)
  • 23位"尾数"
  • 如果指数值为0和255,则可以将值计算为: (sign ? -1 : +1) * 2^exponent * (1.0 + mantissa)
    • 尾数位表示小数分隔符后面二进制数字,例如小数分隔符前面的值未存储但隐式假设为1(如果指数为255,假设为0,但这里不重要),因此对于指数为例如,在图30中,该尾数示例表示该值1001 0000 0000 0000 0000 000 = 2^-1 + 2^-4 = .5 + .0625 = .56251.5625

现在举个例子:

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

  • sign = 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位浮点数的精度来表示!


Eri*_* J. 14

16777217无法用float表示.浮点数可以准确表示的下一个最高数字是16777218.

因此,您尝试将浮点值16777216增加到16777217,这不能在浮点数中表示.

  • 我的投票要求简洁明了。 (2认同)

O. *_*per 13

当您在其二进制表示中查看该值时,您将看到它是一个零和多个​​零,即1 0000 0000 0000 0000 0000 0000,或者恰好是2 ^ 24.这意味着,在16777216,这个数字刚刚增长了一位数.

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

也许,你所看到的是精确度的最后一位数刚刚转移到大于一的位数,所以加一个精确度不再有任何差别.

  • 我可以了解-1的原因吗?我很乐意改善我的答案. (3认同)