Почему GHCi дает неправильный ответ ниже?
GHCi
λ> ((-20.24373193905347)^12)^2 - ((-20.24373193905347)^24)
4.503599627370496e15
python3
>>> ((-20.24373193905347)**12)**2 - ((-20.24373193905347)**24)
0.0
ОБНОВЛЕНИЕ Я бы реализовал функцию Haskell (^) следующим образом.
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
Хотя моя версия не выглядит более правильной, чем приведенная ниже @WillemVanOnsem, она как минимум странно дает правильный ответ для этого конкретного случая.
Python похож.
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
Случайный чувак
источник
источник
a^24
примерно2.2437e31
, и, следовательно, есть ошибка округления, которая производит это.2.243746917640863e31 - 2.2437469176408626e31
которого есть небольшая ошибка округления, которая усиливается. Похоже, вопрос отмены.Ответы:
Краткий ответ : есть разница между
(^) :: (Num a, Integral b) => a -> b -> a
и(**) :: Floating a => a -> a -> a
.(^)
Функция работает только на интегральных показателях. Обычно он использует итеративный алгоритм, который каждый раз проверяет, делится ли мощность на два, и делит мощность на два (и, если не делится, умножает результат наx
). Таким образом, это означает, что для12
, он будет выполнять в общей сложности шесть умножений. Если умножение имеет определенную ошибку округления, эта ошибка может «взорваться». Как мы видим из исходного кода ,(^)
функция реализована следующим образом :(**)
Функция, по крайней мере,Float
с иDouble
ы реализована для работы на устройстве с плавающей точкой. Действительно, если мы посмотрим на реализацию(**)
, мы увидим:Таким образом, это перенаправляет
powerFloat# :: Float# -> Float# -> Float#
функцию, которая обычно будет связана с соответствующей операцией (-ами) FPU компилятором.Если мы используем
(**)
вместо этого, мы получим ноль также для 64-битной единицы с плавающей запятой:Мы можем, например, реализовать итерационный алгоритм в Python:
Если мы затем выполняем ту же операцию, я получаю локально:
Это то же значение, что мы получаем
(^)
в GHCi.источник
pow(..)
функция в Python имеет только определенный алгоритм для "int / long", а не для float. Для поплавков это будет «запасной вариант» по мощности FPU.pow()
функцию.