简短回答: 之间有区别(^) :: (Num a, Integral b) => a -> b -> a https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:-94- and (**) :: Floating a => a -> a -> a https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:-42--42-.
The (^)
函数仅适用于整数指数。它通常会使用迭代算法,每次都会检查幂是否可被二整除,并将幂除以二(如果不可整除,则将结果乘以x
)。因此,这意味着对于12
,它将总共执行six乘法。如果乘法存在一定的舍入误差,则该误差可能会“爆炸”。正如我们在源代码 https://hackage.haskell.org/package/base-4.12.0.0/docs/src/GHC.Real.html#%5E, the (^)函数实现为 https://github.com/ghc/ghc/blob/master/libraries/base/GHC/Float.hs#L377-L396:
(^) :: (Num a, Integral b) => a -> b -> a
x0 ^ y0 | y0 < 0 = errorWithoutStackTrace "Negative exponent"
| y0 == 0 = 1
| otherwise = f x0 y0
where -- f : x0 ^ y0 = x ^ y
f x y | even y = f (x * x) (y `quot` 2)
| y == 1 = x
| otherwise = g (x * x) (y `quot` 2) x -- See Note [Half of y - 1]
-- g : x0 ^ y0 = (x ^ y) * z
g x y z | even y = g (x * x) (y `quot` 2) z
| y == 1 = x * z
| otherwise = g (x * x) (y `quot` 2) (x * z) -- See Note [Half of y - 1]
The (**)
函数是,至少对于Float
s and Double
实现在浮点单元上工作。事实上,如果我们看一下实施(**)
, 我们看:
instance Floating Float where
-- …
(**) x y = powerFloat x y
-- …
这因此重定向到powerFloat# :: Float# -> Float# -> Float# https://hackage.haskell.org/package/base-4.12.0.0/docs/GHC-Exts.html#v:powerFloat-35-函数,通常由编译器链接到相应的 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)
如果我们随后执行相同的操作,我会在本地得到:
>>> pw(pw(-20.24373193905347, 12), 2) - pw(-20.24373193905347, 24)
4503599627370496.0
这与我们得到的值相同(^)
在 GHCi 中。