¿Por qué GHCi da una respuesta incorrecta a continuación?
GHCi
λ> ((-20.24373193905347)^12)^2 - ((-20.24373193905347)^24)
4.503599627370496e15Python3
>>> ((-20.24373193905347)**12)**2 - ((-20.24373193905347)**24)
0.0ACTUALIZACIÓ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.503599627370496e15Aunque 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^24es aproximadamente2.2437e31, y por lo tanto hay un error de redondeo que produce esto.2.243746917640863e31 - 2.2437469176408626e31que 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 -> ay(**) :: 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 paraFloats yDoubles 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.