Pregunta

El siguiente código en C # (.Net 3.5 SP1) es un bucle infinito en mi máquina:

for (float i = 0; i < float.MaxValue; i++) ;

Se alcanza el número 16777216.0 y 16777216.0 + 1 es evalúa a 16777216.0. Sin embargo, en este punto:!. I + 1 = i

Esto es un poco de locura.

Me doy cuenta de que hay alguna inexactitud en cómo se almacenan los números de punto flotante. Y he leído que los números enteros mayor que 2 ^ 24 no puede ser almacenado correctamente como un flotador.

Sin embargo el código anterior, debería ser válido en C #, aunque el número no puede ser adecuadamente representada.

¿Por qué no funciona?

Se puede obtener el mismo a pasar por el doble, pero se necesita un tiempo muy largo. 9007199254740992.0 es el límite para el doble.

¿Fue útil?

Solución

derecho, por lo que el problema es que con el fin de añadir una al flotador, que tendría que convertirse

16777217.0

Lo que pasa es que esto es en el límite de la raíz y no se puede representar exactamente como un flotador. (El siguiente valor más alto disponible es 16777218.0)

Por lo tanto, se redondea al flotador representable más cercano

16777216.0

Déjame ponerlo de esta manera:

Dado que usted tiene un flotante cantidad de precisión, hay que incrementar por una mayor y de mayor número.

EDIT:

Bueno, esto es un poco difícil de explicar, pero intente esto:

float f = float.MaxValue;
f -= 1.0f;
Debug.Assert(f == float.MaxValue);

Esto ejecutará bien, porque en ese valor, con el fin de representar una diferencia de 1.0f, se necesitaría más de 128 bits de precisión. Un flotador tiene sólo 32 bits.

Edit2

Según mis cálculos, al menos 128 dígitos binarios sin signo sería necesario.

log(3.40282347E+38) * log(10) / log(2) = 128

Como una solución a su problema, usted podría bucle a través de dos números de 128 bits. Sin embargo, esto llevará al menos una década para completar.

Otros consejos

Imagine por ejemplo que un número de coma flotante está representado por un máximo de 2 dígitos decimales significativos, además de un exponente: en ese caso, se podría contar de 0 a 99 exactamente. El siguiente sería el 100, pero debido a que sólo puede tener 2 dígitos significativos que se almacenan como "1,0 veces 10 a la potencia de 2". Sumando uno al que sería ... ¿qué?

En el mejor, sería 101 como un resultado intermedio, que en realidad se almacena (a través de un error de redondeo que descarta la tercera dígitos insignificante) como "1,0 veces 10 a la potencia de 2" de nuevo.

Para entender lo que va mal vas a tener que leer el estándar IEEE sobre coma flotante

Vamos a examinar la estructura de un el punto número flotando por un segundo:

Un número de coma flotante se divide en dos partes (ok 3, pero ignorar el bit de signo para un segundo).

Usted tiene un exponente y una mantisa. De esta manera:

smmmmmmmmeeeeeee

Nota:. Que no es bastante precisos para el número de bits, pero te da una idea general de lo que está sucediendo

Para averiguar qué número usted tiene que hacer el siguiente cálculo:

mmmmmm * 2^(eeeeee) * (-1)^s

Entonces, ¿qué se float.MaxValue va a ser? Así que va a tener el mayor mantisa sea posible y el mayor exponente posible. Vamos a suponer que esto se ve algo como:

01111111111111111

en la actualidad definimos NAN y + INF y un par de otras convenciones, pero ignoramos por un segundo porque no son relevantes para su pregunta.

Por lo tanto, lo que ocurre cuando se ha 9.9999*2^99 + 1? Bueno, usted no tiene suficientes cifras significativas para añadir 1. Como resultado, se redondea hacia abajo al mismo número. En el caso de un solo punto flotante de precisión el punto en que comienza a ser +1 redondeado por defecto pasa a ser 16777216.0

No tiene nada que ver con el desbordamiento, o estar cerca del valor máximo. El valor flotante de 16.777.216,0 tiene una representación binaria de 16777216. A continuación, se incrementará en 1, lo que debe ser 16777217.0, excepto que la representación binaria de 16.777.217,0 es 16777216 !!! Para que no se consigue realmente incrementa o, al menos, el incremento no hace lo que usted espera.

Aquí es una clase escrita por Jon Skeet que ilustra esto:

DoubleConverter.cs

Trate de este código con él:

double d1 = 16777217.0;
Console.WriteLine(DoubleConverter.ToExactString(d1));

float f1 = 16777216.0f;
Console.WriteLine(DoubleConverter.ToExactString(f1));

float f2 = 16777217.0f;
Console.WriteLine(DoubleConverter.ToExactString(f2));

Observe cómo la representación interna de 16.777.216,0 16.777.217,0 es lo mismo !!

La iteración cuando i enfoques float.MaxValue tiene i justo por debajo de este valor. La siguiente iteración i se suma a, pero no puede contener un número más grande que float.MaxValue. Por lo tanto, tiene un valor mucho más pequeño, y comienza el bucle de nuevo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top