24

I found a quite strange inconsistency between the behaviour of div and /.

*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

I was quite surprised to notice that the fractional division by zero leads to Infinity, whereas div correctly leads to an exception. A NaN could be acceptable too for /, but why Infinity? There is no mathematical justification for such a result. Do you know the reason for this, please?

Aziz Shaikh
  • 16,245
  • 11
  • 62
  • 79
Riccardo T.
  • 8,907
  • 5
  • 38
  • 78
  • 3
    Mathematically, having the result of `1 / 0` be `Infinity` is completely justified. It is not the only justifiable return value, but the one that makes the most sense. Note that you will also get a `divide by zero` error if you evaluate `1 / 0 :: Rational`. – Daniel Fischer Feb 19 '12 at 23:04
  • 7
    @DanielFischer: I wouldn't call it "mathematically completely justified", as this kind of compactification (with positive and negative infinity) destroys quite a lot of theorems which hold on ℝ, and some of those are assumed in many programs. – leftaroundabout Feb 19 '12 at 23:15
  • 1
    You should not assume things like that when working with floating point numbers. Even basic properties like associativity don't necessarily hold. Equality is also not reflexive for `NaN`s! (E.g. `(0/0) /= (0/0)`. – Tikhon Jelvis Feb 19 '12 at 23:22
  • 2
    @leftaroundabout That's why it's not the only justifiable value of `1/0`. But Alexandrov compactification also destroys many useful properties of ℝ - not to mention Čech compactification. – Daniel Fischer Feb 19 '12 at 23:25
  • @DanielFischer: The result would be justified computing it as the limit of 1/x for x->0^+, but not with just a classical simple division. – Riccardo T. Feb 20 '12 at 12:59
  • So, how do we fix it to give an error for floating point divison by 0, overflow, and other nonsense such as 0 ** 0 ? – Sam Watkins Jan 15 '14 at 15:06

4 Answers4

43

The reason that div does not return Infinity is simple--there is no representation for infinity in the Integer type.

/ returns Infinity because it follows the IEEE 754 standard (which describes floating point number representations) since the default Fractional type is Double. Other languages with floating point numbers (e.g. JavaScript) also exhibit this behavior.

To make mathematicians cringe even more, you get a different result if you divide by negative 0, despite the fact that -0 == 0 for floats:

Prelude> 1/(-0)
-Infinity

This is also behavior from the standard.

If you use a different fractional type like Rational, you will get the behavior you expect:

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

Coincidentally, if you're wondering about why Integer and Double are the types in question when your actual operation does not reference them, take a look at how Haskell handles defaulting types (especially numeric types) in the report.

The short version is that if you have an ambiguous type from the Num class, Haskell will first try Integer and then Double for that type. You can change this with a default (Type1, Type2...) statement or turn it off with a default () statement at the module level.

Tikhon Jelvis
  • 67,485
  • 18
  • 177
  • 214
  • Where can I learn about the `default` statement? I haven't seen it before. – amindfv Feb 20 '12 at 00:50
  • 1
    The section of the report I linked to covers it near the end. I think it's also mentioned in the Gentle Introduction to Haskell. However, I am not sure there is much more to it than I covered here. (Unless you enable some extensions, it only concerns the numeric types and behaves like I explained.) – Tikhon Jelvis Feb 20 '12 at 00:58
  • 1
    I just entered it into GHCi. Which version of GHC do you have? Also, what happens if you just try `1/0`? – Tikhon Jelvis Feb 20 '12 at 07:45
  • @TikhonJelvis: Thanks for your answer, it is quite complete indeed. I have a last doubt: the IEEE standard you mentioned doesn't seem to be the best way to deal with divisions by zero, it kinda assumes them as limits for the denominator that goes to 0 instead of real divisions. Shouldn't be better to just throw an exception, just like `div` does? – Riccardo T. Feb 20 '12 at 13:07
  • 3
    @Riccardo Haskell doesn't specify what should happen when you divide by 0 with floating point numbers. Typically, it will just do a floating point division, so what happens depends on the mode the FPU is in. This is just like C. – augustss Feb 20 '12 at 15:28
6

I hope this helps:

Prelude> 1/0
Infinity
Prelude> -1/0
-Infinity
Prelude> 0/0
NaN
9000
  • 39,899
  • 9
  • 66
  • 104
5

It may not be that way for a mathematical reason. Infinity is used sometimes as a "sin bin": everything that doesn't work in our system cleanly, put it in there.

Example:

Prelude> 10 ** 10 ** 10
Infinity

... is definitely not mathematically justified!

amindfv
  • 8,438
  • 5
  • 36
  • 58
  • 2
    You haven't met a hard core finitist yet! :) – Ingo Feb 20 '12 at 08:25
  • @Ingo Hard core as in zero, one, infinity? – Daniel Fischer Feb 20 '12 at 10:36
  • 1
    @Daniel I mean the people who are maintaining that a number like 10^100 makes no sense as there are not as many objects in the universe. But perhaps they would prefer 10^10^10 = NaN – Ingo Feb 20 '12 at 11:58
  • 2
    @Ingo You can always ask for the number of combinations of objects in the universe and suddenly a lot more numbers are useful for you since you must use the factorial:) – godfryd Feb 20 '12 at 13:30
  • @Piotr - I think it is better to reject their arguments on other grounds. While (10^100)! might indeed be huge, it's still finite, and thus they feel justified in their silliness. – Ingo Feb 20 '12 at 13:38
3

Fractional is not equal to Float (or Double) type.

Fraction of 1/n where n goes to 0 so lim(n→0) 1/n = +∞, lim(n→0) -1/n = -∞ and that makes sense.

David Unric
  • 7,421
  • 1
  • 37
  • 65
  • I think a `Fractional` constraint gets defaulted to `Double`, which is a floating point type. Read the section of the report I linked to. – Tikhon Jelvis Feb 19 '12 at 23:48
  • Correct, @TikhonJelvis, unless there is a default declaration saying otherwise, an ambiguous type with a `Fractional` constraint is defaulted to `Double`. – Daniel Fischer Feb 19 '12 at 23:58