Haskell中(^)的奇怪行为
为什么GHCi在下面给出了不正确的答案 GHCi 蟒蛇3Haskell中(^)的奇怪行为,haskell,floating-point,ghc,ghci,Haskell,Floating Point,Ghc,Ghci,为什么GHCi在下面给出了不正确的答案 GHCi 蟒蛇3 >>> ((-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) | otherwi
>>> ((-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
powerXY::Double->Int->Double
powerXY x 0=1
powerXY x y
|y<0=powerXY(1/x)(-y)
|否则=
设z=powerXY x(y`div`2)
如果y是奇数,那么z*z*x,否则z*z
main=do
设x=-20.24373193905347
打印$powerXY(powerXY x 12)2-powerXY x 24--0
打印$((x^12)^2)-(x^24)--4.50359962737049615
尽管我的版本看起来并不比@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))
def pw(x,y):
如果y<0:
返回pw(1/x,-y)
如果y==0:
返回1
z=pw(x,y//2)
如果y%2==1:
返回z*z*x
其他:
返回z*z
#打印0.0
打印(pw(pw(-20.24373193905347,12),2)-pw(-20.24373193905347,24))
简短回答:和之间有区别
(^)
函数仅对积分指数有效。它通常使用迭代算法,每次检查幂是否可被2整除,并将幂除以2(如果不可整除,则将结果乘以x
)。这意味着对于12
,它将执行总共六次乘法。如果乘法运算有一定的舍入误差,则该误差可能会“爆炸”。正如我们在中所看到的,以下内容:
因此,这将重定向到函数,该函数通常由编译器链接到相应的FPU操作
如果我们改用(**)
,则64位浮点单元也会获得零:
Prelude> (a**12)**2 - a**24
0.0
例如,我们可以用Python实现迭代算法:
def pw(x0, y0):
if y0 < 0:
raise Error()
if y0 == 0:
return 1
return f(x0, y0)
def f(x, y):
if (y % 2 == 0):
return f(x*x, y//2)
if y == 1:
return x
return g(x*x, y // 2, x)
def g(x, y, z):
if (y % 2 == 0):
return g(x*x, y//2, z)
if y == 1:
return x*z
return g(x*x, y//2, x*z)
这与我们在GHCi中获得的
(^)
值相同。这是一个错误,与尾数相反a^24
大约是2.2437e31
,因此有一个舍入误差产生了这种情况。我不明白。为什么GHCi中存在舍入误差?这与GHCi无关,它只是浮点单元处理浮点的方式。这会计算2.243746917640863e31-2.243746917640862e31
,其中舍入误差很小,会被放大。看起来像是一个取消问题。也许python使用了不同的求幂算法,在本例中哪个算法更精确?一般来说,无论使用哪种语言,浮点运算都会出现一些舍入错误。尽管如此,理解这两种算法之间的差异还是很有趣的。在Python中实现的(^)迭代算法不会给出这种舍入错误。Haskell和Python(*)有什么不同?@Randomdude:据我所知,Python中的pow(…)
函数只有一个特定的“int/long”算法,而不是浮点数算法。对于float,它将依靠FPU的强大功能“回退”。我的意思是,当我自己在Python中使用(*)实现power函数时,与Haskell实现(^)的方式相同。我没有使用pow()
函数。@Randomdude:我已经用Python实现了算法,并获得了与ghc相同的值。用Haskell和Python中的(^)版本更新了我的问题。想一想好吗?
instance Floating Float where
-- …
(**) x y = powerFloat x y
-- …
Prelude> (a**12)**2 - a**24
0.0
def pw(x0, y0):
if y0 < 0:
raise Error()
if y0 == 0:
return 1
return f(x0, y0)
def f(x, y):
if (y % 2 == 0):
return f(x*x, y//2)
if y == 1:
return x
return g(x*x, y // 2, x)
def g(x, y, z):
if (y % 2 == 0):
return g(x*x, y//2, z)
if y == 1:
return x*z
return g(x*x, y//2, x*z)
>>> pw(pw(-20.24373193905347, 12), 2) - pw(-20.24373193905347, 24)
4503599627370496.0