2010-01-20 18 views
29

¿Cómo se explica la inexactitud en coma flotante para los programadores nuevos y los legos que todavía piensan que las computadoras son infinitamente acertadas y precisas?
¿Tiene un ejemplo o anécdota favorita que parece transmitir la idea mucho mejor que una explicación precisa pero seca?
¿Cómo se enseña esto en las clases de informática?Ejemplos de imprecisión de coma flotante

+1

Puede verificar esto con esta simple javascript: alerta (0.1 * 0.1 * 10); –

+0

Eche un vistazo a este artículo: [Lo que todo científico informático debe saber sobre la aritmética de punto flotante] (http://docs.sun.com/source/806-3568/ncg_goldberg.html) –

Respuesta

26

Básicamente, hay dos dificultades principales que la gente tropieza con los números de coma flotante.

  1. El problema de la escala. Cada número FP tiene un exponente que determina la "escala" general del número para que pueda representar valores muy pequeños o realmente grandes, aunque la cantidad de dígitos que puede dedicar es limitada. Agregar dos números de diferentes escalas ocasionará que el más pequeño sea "comido", ya que no hay forma de ajustarlo a una escala mayor.

    PS> $a = 1; $b = 0.0000000000000000000000001 
    PS> Write-Host a=$a b=$b 
    a=1 b=1E-25 
    PS> $a + $b 
    1 
    

    Como analogía para este caso, podría imaginarse una gran piscina y una cucharadita de agua. Ambos son de tamaños muy diferentes, pero individualmente se puede captar fácilmente cuánto son aproximadamente. Sin embargo, verter la cucharilla en la piscina lo dejará quieto con aproximadamente una piscina llena de agua.

    (Si las personas que están aprendiendo este tener problemas con la notación exponencial, también se pueden utilizar los valores 1 y 100000000000000000000 más o menos.)

  2. Luego está el problema de la representación decimal binario vs. Un número como 0.1 no se puede representar exactamente con una cantidad limitada de dígitos binarios. Algunos idiomas enmascaran esto, sin embargo:

    PS> "{0:N50}" -f 0.1 
    0.10000000000000000000000000000000000000000000000000 
    

    Pero se puede “amplificar” el error representación añadiendo repetidamente los números juntos:

    PS> $sum = 0; for ($i = 0; $i -lt 100; $i++) { $sum += 0.1 }; $sum 
    9,99999999999998 
    

    No puedo pensar en una buena analogía para explicar adecuadamente este , aunque. Básicamente es el mismo problema por el que puede representar/solo aproximadamente en decimal porque para obtener el valor exacto necesita repetir el 3 indefinidamente al final de la fracción decimal.

    De manera similar, las fracciones binarias son buenas para representar mitades, trimestres, octavos, etc. pero cosas como una décima generarán una secuencia infinitamente repetitiva de dígitos binarios.

  3. Luego hay otro problema, aunque la mayoría de las personas no tropieza con eso, a menos que estén haciendo una gran cantidad de cosas numéricas. Pero entonces, aquellos ya saben sobre el problema. Dado que muchos números de coma flotante son simplemente aproximaciones del valor exacto esto significa que para una aproximación dada f de un número real r no puede ser infinitamente muchos números más reales r , r , ... que se corresponden exactamente con la misma aproximación. Esos números se encuentran en un cierto intervalo.Digamos que r min es el valor mínimo posible de r que se traduce en f y r máximo el valor máximo posible de r para el que esto se mantiene, entonces usted tiene un intervalo [ r min, r max] donde cualquier número en este intervalo puede ser su actual número r.

    Ahora, si realiza cálculos en ese número (suma, resta, multiplicación, etc.), pierde precisión. Cada número es solo una aproximación, por lo tanto, en realidad está realizando cálculos con intervalos. El resultado es un intervalo también y el error de aproximación solo se hace más grande, lo que amplía el intervalo. Puede obtener un solo número de ese cálculo. Pero eso es simplemente un número del intervalo de posibles resultados, teniendo en cuenta la precisión de sus operandos originales y la pérdida de precisión debida al cálculo.

    Ese tipo de cosas se llama Interval arithmetic y al menos para mí era parte de nuestro curso de matemáticas en la universidad.

+1

Hola Johannes, eso es definitivamente una buen ejemplo, pero realmente no le dice a la gente * por qué * no funciona. Estoy buscando hacer que alguien comprenda el motivo de la falla, no solo el hecho de que falla de vez en cuando. –

+1

Hm, aparte de explicar el problema de la escala y el problema de la representación binaria vs. decimal, creo que no he encontrado una mejor manera de decirle esto a las personas: /. Uno podría usar anécdotas similares, como agregar una cucharadita de agua a una piscina no cambia nuestra percepción de cuánto contiene. – Joey

+0

Para dar más detalles, muchas de las personas que recibo en los talleres ni siquiera están muy cómodos con la notación científica, por lo que ya requieren una gran cantidad de esfuerzo mental para cubrir la diferencia entre -4e200, -4e-200, 4e- 200 y 4e200. –

2

en Python:

>>> 1.0/10 
0.10000000000000001 

explican cómo algunas fracciones no se pueden representar con precisión en el sistema binario. Al igual que algunas fracciones (como 1/3) no se pueden representar con precisión en la base 10.

+0

codeape, estoy buscando algo un poco más profundo que simplemente mostrar ejemplos de errores de redondeo. Me gustaría poder decirle a la gente por qué se producen estos errores y hacer que entiendan la razón detrás de esto, sin necesidad de entender la especificación IEEE 754. –

+1

@David: déles un ejemplo donde los números de punto flotante son exactos, como agregar 0,25 varias veces. El resultado será exacto hasta que desborde la mantisa, porque 0.25 es '1/(2^2)'. Luego pruebe lo mismo con 0.2 y obtendrá los problemas, porque 0.2 no es representable en un número finito de base-2. –

6

¿Cómo es esto para una explantación al lego? Una forma en que las computadoras representan números es contando unidades discretas. Estas son computadoras digitales. Para los números enteros, aquellos sin una parte fraccionaria, las computadoras digitales modernas cuentan con poderes de dos: 1, 2, 4, 8. ,,, valor de posición, dígitos binarios, bla, bla, bla. Para las fracciones, las computadoras digitales cuentan con poderes inversos de dos: 1/2, 1/4, 1/8, ... El problema es que muchos números no pueden representarse por una suma de un número finito de esos poderes inversos. El uso de más valores de posición (más bits) aumentará la precisión de la representación de esos números "problemáticos", pero nunca lo obtendrá exactamente porque solo tiene un número limitado de bits. Algunos números no se pueden representar con un número infinito de bits.

Snooze ...

bien, se desea medir el volumen de agua en un recipiente, y sólo tiene 3 tazas de medir: taza completa, media taza y cuarto de taza. Después de contar la última copa llena, digamos que queda un tercio de una taza. Sin embargo, no puede medir eso porque no llena exactamente ninguna combinación de tazas disponibles. No llena la media taza, y el desbordamiento del cuarto de taza es demasiado pequeño para llenar cualquier cosa. Entonces tienes un error: la diferencia entre 1/3 y 1/4. Este error se agrava cuando lo combina con errores de otras mediciones.

8

Demuestre que el sistema base-10 sufre de exactamente el mismo problema.

Trate de representar 1/3 como una representación decimal en la base 10. No podrá hacerlo exactamente.

Así que si escribe "0.3333", tendrá una representación razonablemente exacta para muchos casos de uso.

Pero si lo mueves a una fracción, obtendrás "3333/10000", que es y no lo mismo que "1/3".

Otras fracciones, tales como medio pueden ser fácilmente representado por una representación decimal finita en base 10: "0,5"

Ahora base 2 y la base-10 sufren de esencialmente el mismo problema: ambos tienen algunos números que no pueden representar exactamente.

Mientras que base-10 no tiene ningún problema para representar 1/10 como "0.1" en base-2, necesitaría una representación infinita comenzando por "0,000110011 ..".

2

Otro ejemplo, en C

printf (" %.20f \n", 3.6); 

increíblemente da

3,60000000000000008882

0

Una pieza lindo de rareza numérico se puede observar si uno convierte 9999999.4999999999 a un float y de nuevo a un double. El resultado se informa como 10000000, aunque ese valor es obviamente más cercano a 9999999, y aunque 9999999.499999999 se redondea correctamente a 9999999.

1

Aquí está mi entendimiento simple.

Problema: El valor 0.45 no se puede representar con precisión mediante un flotador y se redondea a 0.450000018. ¿Porqué es eso?

Respuesta: un valor entero de 45 está representada por el valor binario 101101. Con el fin de hacer que el valor de 0,45 sería exacta si usted podría tomar 45 x 10^-2 (= 45/10^2 .) Pero eso es imposible porque debe usar la base 2 en lugar de 10.

Así que lo más cercano a 10^2 = 100 sería 128 = 2^7. El número total de bits que necesita es 9: 6 para el valor 45 (101101) + 3 bits para el valor 7 (111). Entonces el valor 45 x 2^-7 = 0.3515625. Ahora tiene un grave problema de inexactitud. 0.3515625 no está cerca de 0.45.

¿Cómo podemos mejorar esta inexactitud? Bueno, podríamos cambiar el valor 45 y 7 por otra cosa.

¿Qué tal 460 x 2^-10 = 0.44921875. Ahora está utilizando 9 bits para 460 y 4 bits para 10. Luego está un poco más cerca, pero todavía no está tan cerca. Sin embargo, si su valor inicial deseado fuera 0.44921875, entonces obtendría una coincidencia exacta sin aproximación.

Así que la fórmula para su valor sería X = A x 2^B. Donde A y B son valores enteros positivos o negativos. Obviamente, cuanto más altos sean los números, mayor será su precisión, sin embargo, como usted sabe, el número de bits para representar los valores A y B son limitados. Para float tiene un número total de 32. Double tiene 64 y Decimal tiene 128.

Cuestiones relacionadas