¿Por qué GHCi da una respuesta incorrecta a continuación?
GHCi
λ> ((-20.24373193905347)^12)^2 - ((-20.24373193905347)^24)
4.503599627370496e15
Python3
>>> ((-20.24373193905347)**12)**2 - ((-20.24373193905347)**24)
0.0
ACTUALIZACIÓN Implementaría la función de Haskell (^) de la siguiente manera.
powerXY :: Double -> Int -> Double
powerXY x 0 = 1
powerXY x y
| y < 0 = powerXY (1/x) (-y)
| otherwise =
let z = powerXY x (y `div` 2)
in if odd y then z*z*x else z*z
main = do
let x = -20.24373193905347
print $ powerXY (powerXY x 12) 2 - powerXY x 24 -- 0
print $ ((x^12)^2) - (x ^ 24) -- 4.503599627370496e15
Aunque mi versión no parece más correcta que la proporcionada a continuación por @WillemVanOnsem, extrañamente da la respuesta correcta para este caso en particular al menos.
Python es similar.
def pw(x, y):
if y < 0:
return pw(1/x, -y)
if y == 0:
return 1
z = pw(x, y//2)
if y % 2 == 1:
return z*z*x
else:
return z*z
# prints 0.0
print(pw(pw(-20.24373193905347, 12), 2) - pw(-20.24373193905347, 24))
haskell
floating-point
ghc
ghci
Tipo al azar
fuente
fuente
a^24
es aproximadamente2.2437e31
, y por lo tanto hay un error de redondeo que produce esto.2.243746917640863e31 - 2.2437469176408626e31
que tiene un pequeño error de redondeo que se amplifica. Parece un problema de cancelación.Respuestas:
Respuesta corta : hay una diferencia entre
(^) :: (Num a, Integral b) => a -> b -> a
y(**) :: Floating a => a -> a -> a
.La
(^)
función solo funciona en exponentes integrales. Normalmente utilizará un algoritmo iterativo que cada vez verificará si la potencia es divisible por dos, y dividirá la potencia por dos (y si no es divisible multiplica el resultado porx
). Esto significa que para12
, realizará un total de seis multiplicaciones. Si una multiplicación tiene un cierto error de redondeo, ese error puede "explotar". Como podemos ver en el código fuente , la(^)
función se implementa como :La
(**)
función es, al menos paraFloat
s yDouble
s implementado para el trabajo en la unidad de coma flotante. De hecho, si echamos un vistazo a la implementación de(**)
, vemos:Esto, por lo tanto, redirige a la
powerFloat# :: Float# -> Float# -> Float#
función, que normalmente estará vinculada a las operaciones de FPU correspondientes por el compilador.Si usamos en su
(**)
lugar, obtenemos cero también para una unidad de coma flotante de 64 bits:Podemos, por ejemplo, implementar el algoritmo iterativo en Python:
Si luego realizamos la misma operación, obtengo localmente:
Cuál es el mismo valor que lo que obtenemos
(^)
en GHCi.fuente
pow(..)
función en Python solo tiene un cierto algoritmo para "int / long", no para flotantes. Para flotadores, se "retrocederá" en el poder de la FPU.pow()
función.