Por que o GHCi fornece respostas incorretas abaixo?
GHCi
λ> ((-20.24373193905347)^12)^2 - ((-20.24373193905347)^24)
4.503599627370496e15
Python3
>>> ((-20.24373193905347)**12)**2 - ((-20.24373193905347)**24)
0.0
ATUALIZAÇÃO Eu implementaria a função de Haskell (^) da seguinte maneira.
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
Embora minha versão não pareça mais correta do que a fornecida abaixo por @WillemVanOnsem, estranhamente fornece a resposta correta para este caso em particular, pelo menos.
Python é semelhante.
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
Cara aleatória
fonte
fonte
a^24
é aproximadamente2.2437e31
e, portanto, há um erro de arredondamento que produz isso.2.243746917640863e31 - 2.2437469176408626e31
que possui um pequeno erro de arredondamento que é amplificado. Parece um problema de cancelamento.Respostas:
Resposta curta : há uma diferença entre
(^) :: (Num a, Integral b) => a -> b -> a
e(**) :: Floating a => a -> a -> a
.A
(^)
função funciona apenas em expoentes integrais. Normalmente, ele fará uso de um algoritmo iterativo que sempre verificará se a potência é divisível por dois e dividirá a potência por dois (e se não divisível multiplicar o resultadox
). Isso significa que12
, para , ele realizará um total de seis multiplicações. Se uma multiplicação tiver um certo erro de arredondamento, esse erro poderá "explodir". Como podemos ver no código fonte , a(^)
função é implementada como :A
(**)
função é, pelo menos paraFloat
s eDouble
s implementadas para trabalhar na unidade de ponto flutuante. De fato, se dermos uma olhada na implementação de(**)
, veremos:Assim, ele é redirecionado para a
powerFloat# :: Float# -> Float# -> Float#
função, que normalmente será vinculada às operações de FPU correspondentes pelo compilador.Se usarmos
(**)
, obtemos zero também para uma unidade de ponto flutuante de 64 bits:Por exemplo, podemos implementar o algoritmo iterativo em Python:
Se executarmos a mesma operação, eu recebo localmente:
Qual é o mesmo valor que obtemos
(^)
no GHCi.fonte
pow(..)
função em Python só tem um certo algoritmo para "int / long" s, não para carros alegóricos. Para carros alegóricos, ele "recuará" na potência da FPU.pow()
função.