Haskell中的零除错误

24

我发现div/的行为之间存在一种相当奇怪的不一致性。

*ghci> :t 1 `div` 0
1 `div` 0 :: Integral a => a
*ghci> :t 1 / 0
1 / 0 :: Fractional a => a
*ghci> 1 / 0
Infinity
*ghci> 1 `div` 0
*** Exception: divide by zero

我很惊讶地发现分数除以零会导致Infinity,而div则会正确地引发异常。对于/来说,NaN也是可以接受的,但为什么要用Infinity呢?这样的结果没有数学上的理由。请问您知道原因吗?


3
数学上,将 1 / 0 的结果返回为“无穷大”是完全合理的。这不仅是唯一可接受的返回值,但却是最具有意义性的一个。请注意,如果您计算 1 / 0 :: Rational,也会收到“除以零”的错误提示。 - Daniel Fischer
7
我不会称其为“在数学上完全合理”,因为这种紧致化(带有正负无穷)破坏了许多在实数集上成立的定理,其中一些定理被许多程序假定。 - leftaroundabout
1
在处理浮点数时,你不应该做出假设。即使是基本的可结合性等属性也不一定成立。对于“NaN”,相等也不具有自反性!(例如(0/0) /= (0/0))。 - Tikhon Jelvis
2
@leftaroundabout 这就是为什么1/0不是唯一合理的值。但是亚历山大紧致化也破坏了实数集许多有用的特性 - 更不用说Čech紧致化了。 - Daniel Fischer
@DanielFischer:结果可以通过计算x->0^+时的1/x极限来证明,但不能仅使用经典的简单除法。 - Riccardo T.
那么,我们该如何修复它,以便在浮点数除以0、溢出和其他无意义的操作(例如0 ** 0)时报错? - Sam Watkins
4个回答

44
为什么div不返回Infinity呢?原因很简单——在Integer类型中没有表示无穷大的方法。
/返回Infinity是因为它遵循IEEE 754标准(描述浮点数表示),因为默认的Fractional类型是Double。其他支持浮点数的语言(例如JavaScript)也表现出这种行为。
更让数学家不安的是,如果你除以负零,你会得到不同的结果,尽管对于浮点数-0 == 0
Prelude> 1/(-0)
-Infinity

这也是标准行为。

如果您使用不同的分数类型,例如 Rational,则将获得您所期望的行为:

Prelude> 1 / (0 :: Rational)
*** Exception: Ratio.%: zero denominator

巧合的是,如果你想知道为什么在实际操作中没有涉及到IntegerDouble类型,但它们却成为问题所在,可以看一下Haskell如何处理默认类型(特别是数值类型)的方式,参考报告

简单来说,如果你有一个模棱两可的Num类类型,Haskell将首先尝试使用Integer,然后是Double。您可以使用default (Type1,Type2...)语句更改此设置,或使用模块级别的default ()语句禁用它。


我在哪里可以学习关于default语句的知识?我之前从未见过它。 - amindfv
1
我链接的报告部分在结尾处涵盖了它。我认为在Haskell温柔介绍中也提到了它。然而,除非您启用一些扩展,否则它与数字类型有关,并且像我解释的那样运作,我不确定是否还有更多内容需要涵盖。 - Tikhon Jelvis
1
我刚刚在GHCi中输入它。你使用的是哪个版本的GHC?另外,如果你尝试1/0会发生什么? - Tikhon Jelvis
@TikhonJelvis:感谢您的回答,确实非常完整。我还有一个疑问:您提到的IEEE标准似乎不是处理除以零的最佳方式,它将其视为分母趋近于0的极限而不是真正的除法。难道不应该像div一样抛出异常吗? - Riccardo T.
3
Haskell并没有明确说明使用浮点数进行除以0的结果应该是什么。通常情况下,它会执行浮点除法,所以具体结果取决于FPU的模式。这与C语言类似。 - augustss

6
我希望这能帮到您:
Prelude> 1/0
Infinity
Prelude> -1/0
-Infinity
Prelude> 0/0
NaN

谢谢...是的,它看起来就像一个极限放置的方式。 - Riccardo T.

5

这可能并不是由于数学原因。有时候Infinity被用作“垃圾桶”:将所有在我们系统中无法清晰处理的内容放入其中。

例如:

Prelude> 10 ** 10 ** 10
Infinity

这绝对没有数学上的合理性!



2
你还没有遇到过一个真正的有限主义者! :) - Ingo
@Ingo 你说的“Hard core”是指0、1、无穷大吗? - Daniel Fischer
1
@Daniel 我的意思是那些认为像10^100这样的数字没有意义,因为宇宙中没有那么多的物体的人。但也许他们会更喜欢 10^10^10 = NaN。 - Ingo
2
@Ingo,你可以随时询问宇宙中物体的组合数量,这样你就需要使用阶乘了,因为有很多更多的数字对你有用:) - godfryd
@Piotr - 我认为最好以其他理由驳回他们的论点。虽然(10 ^ 100)!确实很大,但它仍然是有限的,因此他们感到自己的愚蠢是合理的。 - Ingo

3

分数类型并不等同于浮点数(或双精度浮点数)类型。

当n趋近于0时,1/n的极限为正无穷,-1/n的极限为负无穷,这是有道理的。


我认为“Fractional”约束会默认为“Double”,这是一种浮点类型。请阅读我链接的报告部分。 - Tikhon Jelvis
正确的,@TikhonJelvis,除非有一个默认声明表明不同,否则带有“Fractional”约束的模糊类型将默认为“Double”。 - Daniel Fischer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接