【问题标题】:Why does a float variable stop incrementing at 16777216 in C#?为什么浮点变量在 C# 中的 16777216 处停止递增?
【发布时间】:2012-09-17 18:45:52
【问题描述】:
float a = 0;
while (true)
{
    a++;
    if (a > 16777216)
        break; // Will never break... a stops at 16777216
}

谁能向我解释为什么在这段代码中浮点值在 16777216 处停止递增?

编辑:

或者更简单:

float a = 16777217; // a becomes 16777216

【问题讨论】:

标签: c# floating-point


【解决方案1】:

16777217 不能用浮点数精确表示。浮点数可以精确表示的下一个最大数字是 16777218。

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

【讨论】:

    【解决方案2】:

    当您以二进制表示形式查看该值时,您会发现它是一个多个零,即1 0000 0000 0000 0000 0000 0000,或者正好是 2^24。这意味着,在 16777216,这个数字刚刚增长了一位数。

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

    您可能看到的是,精度的最后一位刚刚转移到大于 1 的值,因此添加一个不再有任何区别。

    【讨论】:

    • 我可以知道 -1 的原因吗?我很乐意改进我的答案。
    • 即使我可能不知道第一次投反对票的原因,请问我可以询问第二次投反对票的原因吗?
    【解决方案3】:

    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 正好是 224,并且会像这样表示为 32 位浮点数:

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

    现在让我们看看数字 16777217,或者正好是 224+1:

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

    【讨论】:

      【解决方案4】:

      想象一下十进制形式。假设你有号码:

      1.000000 * 10^6
      

      或 1,000,000。如果你只有六位数的准确度,那么在这个数字上加上 0.5 就会得到

      1.0000005 * 10^6
      

      但是,当前使用 fp 舍入模式的想法是使用“舍入到偶数”而不是“舍入到最近”。在这种情况下,每次增加此值时,它都会以浮点单位向下舍入回 16,777,216 或 2^24。 IEE 754 中的 Singles 表示为:

      +/- exponent (1.) fraction
      

      “1”在哪里。是隐含的,在这种情况下,分数是另外 23 位,全为零。额外的二进制 1 将溢出到保护位,向下执行舍入步骤,并且每次都被删除,无论您增加多少次。 ulp最后一个位置的单位 将始终为零。最后一次成功的增量来自:

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

      【讨论】:

        猜你喜欢
        • 2016-06-11
        • 1970-01-01
        • 1970-01-01
        • 2017-01-24
        • 2018-03-29
        • 2016-11-19
        • 2021-07-21
        • 2014-04-12
        • 1970-01-01
        相关资源
        最近更新 更多