Alcance y flotadores Haskell

Resuelto undur_gongor asked hace 13 años • 2 respuestas

¿Por qué el comportamiento de la notación de rango de Haskell es diferente para flotantes que para números enteros y caracteres?

Prelude> [1, 3 .. 10] :: [Int]
[1,3,5,7,9] 
Prelude> [1, 3 .. 10] :: [Float]
[1.0,3.0,5.0,7.0,9.0,11.0]
Prelude> ['a', 'c' .. 'f']
"ace"

Lo entendería si el último elemento estuviera cerca del límite superior, pero obviamente esto no es una cuestión de redondeo.

undur_gongor avatar Sep 03 '11 07:09 undur_gongor
Aceptado

La sintaxis [e1, e2 .. e3]es realmente azúcar sintáctica para enumFromThenTo e1 e2 e3, que es una función en la Enumclase de tipos.

El estándar Haskell define su semántica de la siguiente manera:

Para los tipos Inty Integer, las funciones de enumeración tienen el siguiente significado:

  • La secuencia enumFrom e1es la lista [e1,e1 + 1,e1 + 2,…].
  • La secuencia enumFromThen e1 e2es la lista [e1,e1 + i,e1 + 2i,…], donde el incremento, ies e2 − e1. El incremento puede ser cero o negativo. Si el incremento es cero, todos los elementos de la lista son iguales.
  • La secuencia enumFromTo e1 e3es la lista [e1,e1 + 1,e1 + 2,…e3]. La lista está vacía si e1 > e3.
  • La secuencia enumFromThenTo e1 e2 e3es la lista [e1,e1 + i,e1 + 2i,…e3], donde el incremento, ies e2 − e1. Si el incremento es positivo o cero, la lista termina cuando el siguiente elemento sea mayor que e3; la lista está vacía si e1 > e3. Si el incremento es negativo, la lista termina cuando el siguiente elemento sea menor que e3; la lista está vacía si e1 < e3.

Esto es más o menos lo que cabría esperar, pero las instancias Floaty Doublese definen de forma diferente:

Para Floaty Double, la semántica de la enumFromfamilia viene dada por las reglas Intanteriores, excepto que la lista termina cuando los elementos se vuelven mayores que e3 + i∕2para el incremento positivo i, o cuando se vuelven menores que e3 + i∕2para el negativo i.

No estoy realmente seguro de cuál es la justificación para esto, así que la única respuesta que puedo darle es que es así porque está definido así en el estándar.

Puede solucionar este problema enumerando el uso de números enteros y convirtiéndolos Floatposteriormente.

Prelude> map fromIntegral [1, 3 .. 10] :: [Float]
[1.0,3.0,5.0,7.0,9.0]
hammar avatar Sep 03 '2011 01:09 hammar

Ok, @Henning Makholm ya dijo esto en su comentario, pero no explicó por qué en realidad es una mejor solución.

Lo primero que debemos decir: cuando trabajamos con punto flotante, siempre debemos estar atentos a los posibles errores de redondeo. Cuando escribamos [0.0, 0.1 .. 1.0]debemos ser conscientes de que todos estos números, excepto el primero, no estarán en el lugar exacto de las décimas. Cuando necesitemos este tipo de certeza, no debemos utilizar flotadores en absoluto.

Pero, por supuesto, hay muchas aplicaciones en las que nos contentamos con una certeza razonable , pero necesitamos alta velocidad. Ahí es donde las carrozas son geniales. Una posible aplicación de dicha lista sería una integración numérica trapezoidal simple:

trIntegrate f l r s = sum [ f x | x<-[l,(l+s)..r] ] * s - (f(l)+f(r))*s/2

Probemos esto: trIntegrate ( \x -> exp(x + cos(sqrt(x) - x*x)) ) 1.0 3.0 0.1 => 25,797334337026466
comparado con 25,9144, un error de menos del uno por ciento. No es exacto, por supuesto, pero eso es inherente al método de integración.

Supongamos ahora que los rangos flotantes se definieron para terminar siempre al cruzar el borde derecho. Entonces, sería posible (¡pero no podemos estar seguros de ello!) que solo se calculen 20 valores en lugar de 21 en la suma, porque el último valor de xresulta ser 3,000000 y tantos. Podemos simular esto

bad_trIntegrate f l r s = sum [ f x | x<-[l,(l+s)..(r-s)] ] * s - (f(l)+f(r))*s/2

entonces obtenemos

bad_trIntegrate ( \x -> exp(x + cos(sqrt(x) - x*x)) ) 1.0 3.0 0.1

=> 21.27550564546988
¡Urgh!

Esto no tiene nada que ver con ocultar los problemas del punto flotante. Es sólo un método para ayudar al programador a solucionar estos problemas más fácilmente. De hecho, ¡el resultado contradictorio de [1, 3 .. 10] :: Float ayuda a recordar estos problemas!

leftaroundabout avatar Sep 03 '2011 22:09 leftaroundabout