3

只是为了好玩,我想看看如果我在 Haskell 中定义一个函数会发生什么Int -> Int,因为知道它会溢出并且必须返回一个Integer. 考虑以下:

factorial :: Int -> Int
factorial n = product [1..n]

现在我知道,如果我运行factorial 50,我将在 的 'codomain' 之外获得一个数字factorial。由于 Haskell 的类型非常强,我希望它会返回错误。相反,GHCi 返回一个奇怪的否定Int

ghci> factorial 50
-3258495067890909184

请注意

ghci> maxBound :: Int
9223372036854775808

因为我在 64 位上运行。

我觉得这种行为有点可怕:为什么 Haskell 不引发错误?为什么会factorial 50返回一个随机负数?任何澄清将不胜感激。

4

1 回答 1

20

Int类型被定义为“至少具有 [-2^29 .. 2^29-1] 范围的固定精度整数类型”。[0] 范围因机器而异,但您可以使用minBoundmaxBound

它溢出的原因是它为它分配了固定数量的内存。想象一个更简单的例子——内存中的一个正整数,最大值为 7(在内存中存储为 3 位)

0 表示为 000,二进制
1 表示为 001
2 表示为 010,以此类推。

请注意位数学的工作原理:当您加 1 时,您要么将最小的数字从 0 变为 1,要么将其从 1 变为 0,并对下一个最重要的数字执行相同的操作。

例如,011 + 1100

现在,如果你天真地(就像 Haskell 所做的那样)在没有下一个最重要的数字时执行此操作,那么它只会像往常一样递增,但“会被砍掉头”。例如111 + 1变成000而不是1000。这就是 Haskell 正在发生的事情,除了它的最低值(表示为一系列0s)是它的最低负数。它使用它的“最左边”位来表示 +/-。你需要做(maxBound :: Int) + (maxBound :: Int) + 2才能得到 0。

所以类似地:

>  maxBound :: Int  
9223372036854775807  
>  (maxBound :: Int) + 1  
-9223372036854775808  
>  (maxBound :: Int) + 2  
-9223372036854775807  
>  let x = (maxBound :: Int) + 1 in x + x
0

为什么“允许”这种情况发生?简单——效率。不检查是否会有整数溢出要快得多。这就是Integer存在的原因——它是无限的,因为当你认为你可能会溢出时,你会为效率付出代价。

[0] http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-Int.html

于 2013-07-20T20:53:14.713 回答