Pregunta

He estado yendo a través de un tutorial Haskell recientemente y se dio cuenta este comportamiento cuando se trata de algunas expresiones simples Haskell en la cáscara ghci interactivo:

Prelude> 1.1 + 1.1 == 2.2
True
Prelude> 1.1 + 1.1 + 1.1 == 3.3
False
Prelude> 1.1 + 1.1 + 1.1 > 3.3
True
Prelude> 1.1 + 1.1 + 1.1
3.3000000000000003

¿Alguien sabe por qué es así?

¿Fue útil?

Solución

Debido 1.1 y 3.3 son números de punto flotante . Las fracciones decimales, tales como 0.1 o 0.3, no son exactamente representable en un número de punto flotante binario. 0,1 significa 1/10. Para representar que en binario, donde cada dígito fraccional representa 1/2 n (1/2, 1/4, 1/8, etc.), se necesitaría un número infinito de dígitos, 0.000110011 .. . repitiendo infinitamente.

Este es exactamente el mismo problema que el que representa, por ejemplo, un tercio de la base 10. En la base 10, se necesitaría un número infinito de dígitos, .33333 ... para siempre, para representar exactamente 1/3. Así que trabajar en base 10, por lo general ronda, a algo así como 0,33. Pero si se suman tres copias de ese, se obtiene 0.99, no 1.

Para obtener más información sobre el tema, lea Lo que todo científico debe ordenador saber acerca de Punto flotante aritmética .

Para representar números racionales más precisamente en Haskell, siempre se puede utilizar el tipo de datos racional, Ratio ; junto con bignums (arbitrariamente grandes números enteros, Integer en Haskell, a diferencia de Int que están fijadas tamaño) como el tipo de numerador y denominador, puede representar números racionales arbitrariamente precisas, pero a una velocidad significativamente más lenta que los números de punto flotante, que son implementado en hardware y optimizado para la velocidad.

Los números de punto flotante son una optimización, para la computación científica y numérica, que el comercio fuera de precisión para alta velocidad, lo que permite realizar un gran número de cálculos en un pequeño tiempo, siempre y cuando usted es consciente de redondeo y cómo se afecta sus cálculos.

Otros consejos

Debido a que los números de coma flotante no son exactos (Wikipedia)

Puede evitar errores de punto flotante en Haskell usando tipos racionales:

Prelude Data.Ratio> let a = (11 % 10) + (11 % 10) + (11 % 10)
Prelude Data.Ratio> a > (33 % 10)
False
Prelude Data.Ratio> fromRational a
3.3

Por supuesto, paga una multa de rendimiento para el aumento de la precisión.

Parece un problema de error de punto flotante típico.

¿Cuál es un ejemplo simple de punto flotante / error de redondeo?

Tiene que ver con la forma de trabajo de IEEE números de punto flotante.

1.1 se representa como 1.1000000000000001 en coma flotante, 3.3 se representa como 3.2999999999999998.

Así 1.1 + 1.1 + 1.1 es en realidad

1.1000000000000001 + 1.1000000000000001 + 1.1000000000000001 = 3.3000000000000003

Lo cual, como se puede ver es en realidad más grande que 3.2999999999999998.

La solución habitual es que o bien no evaluar la igualdad, o para comprobar si un número está dentro del objetivo de +/- un pequeño épsilon (que define la precisión que necesita).

Ex: si ambos son verdad, entonces la suma es "igual" a 3.3 (dentro del error permitido).

1.1 + 1.1 + 1.1 < 3.3 + 1e9 
1.1 + 1.1 + 1.1 > 3.3 - 1e9

Pocos flotadores se pueden expresar con exactitud mediante IEEE representación 754, por lo que siempre será un poco apagado.

En general, usted no debe comparando flotadores para la igualdad (por las razones expuestas más arriba). La única razón que se me ocurre es que si quieres decir "ha cambiado este valor?" Por ejemplo, "Si (NewsCore / = oldscore)", entonces tomar alguna acción. Eso está bien, siempre y cuando no se está comparando el resultado de dos cálculos separados para comprobar si resultan ser iguales (porque entonces incluso si son matemáticamente, puede ser que completan lo contrario).

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