Dlaczego GHCi podaje poniżej nieprawidłową odpowiedź?
GHCi
λ> ((-20.24373193905347)^12)^2 - ((-20.24373193905347)^24)
4.503599627370496e15
Python3
>>> ((-20.24373193905347)**12)**2 - ((-20.24373193905347)**24)
0.0
AKTUALIZACJA Zaimplementowałbym funkcję Haskella (^) w następujący sposób.
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
Chociaż moja wersja nie wydaje się bardziej poprawna niż ta podana poniżej przez @WillemVanOnsem, dziwnie daje co najmniej poprawną odpowiedź dla tego konkretnego przypadku.
Python jest podobny.
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
Losowy koleś
źródło
źródło
a^24
jest w przybliżeniu2.2437e31
, a zatem występuje błąd zaokrąglania, który to powoduje.2.243746917640863e31 - 2.2437469176408626e31
który ma mały błąd zaokrąglania, który zostaje wzmocniony. Wygląda na problem anulowania.Odpowiedzi:
Krótka odpowiedź : istnieje różnica między
(^) :: (Num a, Integral b) => a -> b -> a
i(**) :: Floating a => a -> a -> a
.(^)
Funkcja działa tylko na integralnych wykładników. Zwykle korzysta z iteracyjnego algorytmu, który za każdym razem sprawdza, czy moc jest podzielna przez dwa, i dzieli moc przez dwa (a jeśli niepodzielna pomnoży wynikx
). Oznacza to zatem, że dla12
wykona w sumie sześć multiplikacji. Jeśli mnożenie ma określony błąd zaokrąglania, błąd ten może „wybuchnąć”. Jak widzimy w kodzie źródłowym ,(^)
funkcja jest implementowana jako :(**)
Funkcja jest, przynajmniej dlaFloat
siDouble
y wdrożony do pracy na jednostkę zmiennoprzecinkową. Rzeczywiście, jeśli spojrzymy na wdrożenie(**)
, zobaczymy:W ten sposób przekierowuje się do
powerFloat# :: Float# -> Float# -> Float#
funkcji, która normalnie będzie połączona z odpowiednimi operacjami FPU przez kompilator.Jeśli użyjemy
(**)
zamiast tego, otrzymamy również zero dla 64-bitowej jednostki zmiennoprzecinkowej:Możemy na przykład zaimplementować algorytm iteracyjny w Pythonie:
Jeśli następnie wykonamy tę samą operację, otrzymam lokalnie:
Jest to ta sama wartość, co otrzymujemy
(^)
w GHCi.źródło
pow(..)
funkcja w Pythonie ma tylko pewien algorytm dla „int / long”, a nie dla float. W przypadku pływaków „spadnie” na moc FPU.pow()
funkcji.