为什么此代码会因Int-> Int溢出?

时间:2019-06-01 18:43:52

标签: haskell

我正在使用递归调用来计算指数。它工作到2 ** 63,然后归零。我认为我有些溢出。但是我以为Haskell处理了这个问题。

我尝试使用数字直到64,然后检查到Int。

power :: Int -> Int
power n = if n==0 then 1 else 2*power(n-1)
main = return()

在GHCI中

1152921504606846976
*Main> power 70
0
*Main> power 65
0
*Main> power 64
0
*Main> power 63
-9223372036854775808
*Main> power 62
4611686018427387904

1 个答案:

答案 0 :(得分:6)

  

我认为我有些溢出。但是我以为Haskell处理了这个问题。

确实是溢出的,Haskell具有一种可以处理任意大小的整数的类型:Integer。但是,您使用Int。如文档所指定,对于Int

  

data Int

     

一种固定精度整数类型,其范围至少为[-2^29 .. 2^29-1]。可以通过使用minBound类中的maxBoundBounded来确定给定实现的确切范围。

Int因此具有固定的字长,并且可能会溢出。您可以使用Integer,但是它可以代表任意数字(直到内存用完为止)。

如果因此将定义更改为:

power :: Integer -> Integer
power 0 = 1
power n = 2 * power (n-1)

我们确实可以计算 2 128

Prelude> power 128
340282366920938463463374607431768211456

请注意,我们可以通过以下方法提高此power函数的性能:

power :: Integral i => i -> Integer
power 0 = 1
power n | even n = p * b2
        | otherwise = 2 * b2 * b2
    where b2 = power (div n 2)

这成立,因为 b 2 a =(b 2)a 。因此,如果我们假设所有操作都在恒定时间内进行,则此算法在 O(log p)中运行。但是,这并不完全成立,因为b2可能会很大,因此乘以b2不会在恒定时间内运行。