Warum gibt GHCi unten eine falsche Antwort?
GHCi
λ> ((-20.24373193905347)^12)^2 - ((-20.24373193905347)^24)
4.503599627370496e15
Python3
>>> ((-20.24373193905347)**12)**2 - ((-20.24373193905347)**24)
0.0
UPDATE Ich würde Haskells (^) Funktion wie folgt implementieren.
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
Obwohl meine Version nicht korrekter erscheint als die unten von @WillemVanOnsem bereitgestellte, gibt sie zumindest für diesen speziellen Fall seltsamerweise die richtige Antwort.
Python ist ähnlich.
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
Zufälliger Typ
quelle
quelle
a^24
ist ungefähr2.2437e31
, und daher gibt es einen Rundungsfehler, der dies erzeugt.2.243746917640863e31 - 2.2437469176408626e31
was einen kleinen Rundungsfehler hat, der verstärkt wird. Sieht aus wie ein Stornierungsproblem.Antworten:
Kurze Antwort : Es gibt einen Unterschied zwischen
(^) :: (Num a, Integral b) => a -> b -> a
und(**) :: Floating a => a -> a -> a
.Die
(^)
Funktion funktioniert nur bei integralen Exponenten. Normalerweise wird ein iterativer Algorithmus verwendet, der jedes Mal prüft, ob die Leistung durch zwei teilbar ist, und die Leistung durch zwei teilt (und wenn nicht teilbar, das Ergebnis mit multipliziertx
). Dies bedeutet also, dass für12
insgesamt sechs Multiplikationen durchgeführt werden. Wenn eine Multiplikation einen bestimmten Rundungsfehler aufweist, kann dieser Fehler "explodieren". Wie wir im Quellcode sehen können , ist die(^)
Funktion wie folgt implementiert :Die
(**)
Funktion ist zumindest fürFloat
s undDouble
s implementiert, um an der Gleitkommaeinheit zu arbeiten. Wenn wir uns die Implementierung von ansehen(**)
, sehen wir in der Tat :Dies leitet somit zu der
powerFloat# :: Float# -> Float# -> Float#
Funktion weiter, die normalerweise vom Compiler mit den entsprechenden FPU-Operationen verknüpft wird.Wenn wir
(**)
stattdessen verwenden, erhalten wir auch Null für eine 64-Bit-Gleitkommaeinheit:Wir können zum Beispiel den iterativen Algorithmus in Python implementieren:
Wenn wir dann die gleiche Operation ausführen, erhalte ich lokal:
Welches ist der gleiche Wert wie das, was wir
(^)
in GHCi bekommen.quelle
pow(..)
Funktion in Python nur einen bestimmten Algorithmus für "int / long", nicht für Floats. Bei Schwimmern wird die Leistung der FPU "zurückgefallen".pow()
Funktion.