18

I found myself having a case where the equivalent of floor $ 1/0 was being executed.

λ> 1/0
Infinity

This is normal behavior as far as I understand but, when Infinity is floor'd or ceiling'd

λ> floor $ 1/0   
179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216

Instead of failing, this very big number is produced. Why?

Maybe more importantly, how can I distinguish this from a non faulty result without using a filter before applying another function?

farhanhubble
  • 438
  • 3
  • 18
gxtaillon
  • 940
  • 1
  • 17
  • 29
  • 1
    the number appears to be `2^1024 :: Integer`, I don't know why that number in particular though. – Wes Jun 21 '14 at 06:48
  • 4
    Because someone forgot to test for infinity in the code for `properFraction`. – augustss Jun 21 '14 at 09:35
  • @Wes Well, Infinity is represented by 2 ^ 1023 in the IEEE 754 standard. It might have something to do with it. – Shoe Jun 29 '14 at 03:14
  • @augustss The Haskell language report does not specify the behavior when passing `Infinite` to `properFraction`. Specifically at §6.4.6 it states "The function `properFraction` **takes a real fractional number** x and returns...". `Infinite` is not a number. So no, nobody forgot to test for infinity. The behavior is simply undefined in the language report (you should test for infinity yourself). – Shoe Jun 29 '14 at 03:17
  • 1
    @Jefffrey, isn't a "real fractional number" just any member of a type in class `RealFrac`? – dfeuer Jun 29 '14 at 05:02
  • Like augustss said, is it possible that the spec simply forgot that use case?. – gxtaillon Jun 29 '14 at 05:58
  • 5
    @Jeffrey I didn't just make up what happened, I know what happened. When Haskell was first defined IEEE-754 floating point was not dominant like it is now, so there was no concern or thought about infinities or NaN. I wrote the first implementation (that I know of) of `properFraction` for `Float` and `Double` and I forgot to test for infinity. And subsequent re-implementations have repeated the error. – augustss Jun 29 '14 at 11:06

1 Answers1

9

The first question is perhaps not so important, so I'll try to answer the second question first.

Once you have a number, if you know that it came from floor x, you can't know whether x was the valid representation of 2^1024 or if it was infinity. You can probably assume anything outside of the range of double is invalid and was produced from infinity, negative infinity, NaN or the like. It would be quite simple to check if your value is valid using one/many of the functions in RealFloat, like isNaN, isInfinite, etc.

You could also use something like data Number a = N a | PosInf | NegInf. Then you write:

instance RealFrac a => RealFrac (Number a) where 
  ...
  floor (N n) = floor n
  floor PosInf = error "Floor of positive infinity"
  floor NegInf = error "Floor of negative infinity"
  ..

Which approach is best is based mostly on your use case.

Maybe it would be correct for floor (1/0) to be an error. But the value is garbage anyways. Is it better to deal with garbage or an error?

But why 2^1024? I took a look at the source for GHC.Float:

properFraction (F# x#)
  = case decodeFloat_Int# x# of
    (# m#, n# #) ->
        let m = I# m#
            n = I# n#
        in
        if n >= 0
        then (fromIntegral m * (2 ^ n), 0.0)
        else let i = if m >= 0 then                m `shiftR` negate n
                               else negate (negate m `shiftR` negate n)
                 f = m - (i `shiftL` negate n)
             in (fromIntegral i, encodeFloat (fromIntegral f) n)

floor x     = case properFraction x of
                (n,r) -> if r < 0.0 then n - 1 else n

Note that decodeFloat_Int# returns the mantissa and exponent. According to wikipedia:

Positive and negative infinity are represented thus: sign = 0 for positive infinity, 1 for negative infinity. biased exponent = all 1 bits. fraction = all 0 bits.

For Float, this means a base of 2^23, since there are 23 bits in the base, and an exponent of 105 (why 105? I actually have no idea. I would think it should be 255 - 127 = 128, but it seems to actually be 128 - 23). The value of floor is fromIntegral m * (2 ^ n) or base*(2^exponent) == 2^23 * 2^105 == 2^128. For double this value is 1024.

user2407038
  • 13,827
  • 2
  • 25
  • 39
  • 1
    This isn't really relevant, but why on Earth does `properFraction` unbox its argument, decompose it, and then box up the pieces again before actually using them? I'm staring at that code and wondering how it makes any sense at all. – dfeuer Jun 29 '14 at 05:07
  • Beyond that, what's up with `shiftR m (negate n)`? The documentation gives no guarantee whatsoever of the result of that shift if `negate n` is large. If GHC is going to rely on weird undocumented behavior like that, why not use an explicitly unsafe shift? – dfeuer Jun 29 '14 at 05:18