【问题标题】:Dubious 'Int' vs 'Integer' handling in Haskell?Haskell中可疑的'Int'与'Integer'处理?
【发布时间】:2013-07-19 22:32:50
【问题描述】:

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

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

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

ghci> factorial 50
-3258495067890909184

请注意

ghci> maxBound :: Int
9223372036854775808

因为我在 64 位上运行。

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

【问题讨论】:

  • 每次计算时检查一个 int 是否超出范围是完全荒谬的并且会破坏性能。如果您有可能超出范围的功能,请使用Integer。阶乘是这样做的合适时机,因为它可以如此迅速地创建大值。
  • 我显然知道它会提前超出范围(你读过帖子吗?)。问题是为什么我得到一个可疑的负数而不是 Int 的上限。
  • 您为什么希望它返回Integer?这是一个有效的Int -> Int -> Int,而您的答案是product 在Int 上运行,是Int。根据Int 的所有定义,您的答案是非常合法的Int。
  • 顺便说一下,它不是一个随机的负数。这正是人们所期望的(对于 GHC 上的 64 位 Ints),50! 以 2^64 为模的余数在 -2^63 到 2^63-1 的范围内。
  • @JustinL。我不一定期望它会返回Int。我只知道它会溢出。

标签: haskell types integer


【解决方案1】:

Int 类型定义为“至少具有 [-2^29 .. 2^29-1] 范围的固定精度整数类型。” [0] 范围因机器而异,但您可以通过minBound 和maxBound 找到它

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

0 表示为 000,二进制
1 表示为 001
2表示为010等。

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

例如,011 + 1 是 100。

现在,如果你天真地(就像 Haskell 那样)在 no 下一个最重要的数字时执行此操作,那么它只会像往常一样增加,但“会被砍掉头”。例如。 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

【讨论】:

  • 一个小问题——您为有符号整数(偏移二进制)描述的系统实际上在大多数 CPU 架构中非常罕见。在大多数现代架构上,最小的负数实际上通常是100...。见en.wikipedia.org/wiki/Signed_number_representations 和en.wikipedia.org/wiki/Two%27s_complement
  • 二进制补码的巧妙之处在于它允许这种“环”结构仍然存在。取最大的正数0111,然后加一,得到最小的负数1000。从那时起,你就在数数;在1111,你在-1,再加一个,你得到0000(零)。
  • @JustinL。绝对,我从来没有听说过最低的数字都是空位。这可能是对海报的误解。检查shiftR (maxBound :: Int) 63 应该给出0,而不是minBound+1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-26
  • 1970-01-01
  • 1970-01-01
  • 2012-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多