¿Por qué una variable flotante deja de incrementarse a 16777216 en C#?


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

¿Puede alguien explicarme esto por qué un valor flotante deja de incrementarse en 16777216 en este código?

Editar:

O aún más simple:

float a = 16777217; // a becomes 16777216
Author: user, 2012-09-26

4 answers

Resumen corto de los números de coma flotante IEEE-754 (32 bits) en la parte superior de mi cabeza:

  • 1 signo de bit (0 significa número positivo, 1 significa número negativo)
  • exponente de 8 bits (con sesgo de -127, no es importante aquí)
  • 23 bits "mantissa"
  • Con excepciones para los valores del exponente 0 y 255, puede calcular el valor como: (sign ? -1 : +1) * 2^exponent * (1.0 + mantissa)
    • Los bits mantissa representan binario dígitos después de el separador decimal, por ejemplo, 1001 0000 0000 0000 0000 000 = 2^-1 + 2^-4 = .5 + .0625 = .5625 y el valor delante del separador decimal no se almacena sino que se asume implícitamente como 1 (si el exponente es 255, se asume 0 pero eso no es importante aquí), por lo que para un exponente de 30, por ejemplo, este ejemplo de mantisa representa el valor 1.5625

Ahora a su ejemplo:

16777216 es exactamente 224, y sería representado como flotador de 32 bits así:

  • signo = 0 (número positivo)
  • exponente = 24 (almacenado como 24+127=151=10010111)
  • mantissa = .0
  • Como representación de punto flotante de 32 bits: 0 10010111 00000000000000000000000
  • Por lo tanto: Valor = (+1) * 2^24 * (1.0 + .0) = 2^24 = 16777216

Ahora veamos el número 16777217, o exactamente 224+1:

  • signo y exponente son lo mismo
  • mantissa tendría que ser exactamente 2-24 de modo que (+1) * 2^24 * (1.0 + 2^-24) = 2^24 + 1 = 16777217
  • Y aquí está el problema. La mantisa no puede tener el valor 2-24 porque solo tiene 23 bits, por lo que el ¡el número 16777217 simplemente no se puede representar con la precisión de los números de puntos flotantes de 32 bits!
 48
Author: AndiDog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-26 09:15:01

16777217 no se puede representar exactamente con un flotador. El siguiente número más alto que un flotador puede representar exactamente es 16777218.

Por lo tanto, intenta incrementar el valor de flotación 16777216 a 16777217, que no se puede representar en un flotador.

 11
Author: Eric J.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-26 07:33:41

Cuando usted mira ese valor en su representación binaria, usted verá que es un uno y muchos ceros, a saber 1 0000 0000 0000 0000 0000 0000, o exactamente 2^24. Eso significa, en 16777216, el número acaba de crecer en un dígito.

Como es un número en coma flotante, esto puede significar que el último dígito en su extremo que todavía está almacenado (es decir, dentro de su precisión) también se desplaza a la izquierda.

Probablemente, lo que estás viendo es que el último dígito de precisión acaba de cambiar a algo más grande que uno, así que agregar uno ya no hace ninguna diferencia.

 11
Author: O. R. Mapper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-26 07:34:05

Imagine esto en forma decimal. Supongamos que usted tiene el número:

1.000000 * 10^6

O 1.000.000. Si todo lo que tuviera fueran seis dígitos de precisión, agregar 0.5 a este número produciría

1.0000005 * 10^6

Sin embargo, el pensamiento actual con los modos de redondeo fp es usar "Round to Even" en lugar de "Round to Nearest"."En este caso, cada vez que se incrementa este valor, se redondeará hacia abajo en la unidad de coma flotante de nuevo a 16,777,216, o 2^24. Singles en IEE 754 está representado as:

+/- exponent (1.) fraction

Donde el "1."está implícito y la fracción es otros 23 bits, todos ceros, en este caso. El binario adicional 1 se derramará en el dígito de guardia, se llevará hasta el paso de redondeo y se eliminará cada vez, sin importar cuántas veces lo incremente. La unidad ulp o en el último lugar siempre será cero. El último incremento exitoso es de:

+2^23 * (+1.) 11111111111111111111111 -> +2^24 * (1.) 00000000000000000000000
 2
Author: Max,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-03 02:13:06