The Haskell type class RealFloat has a reasonable number of operations to test for NaN, denormalized numbers, etc.<br>You can also ask if the implementation uses IEEE.<br><br> -- Lennart<br><br><div><span class="gmail_quote">
On 8/4/07, <b class="gmail_sendername">Paul Johnson</b> <<a href="mailto:paul@cogito.org.uk">paul@cogito.org.uk</a>> wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Andrew Coppin wrote:<br>> Paul Johnson wrote:<br>>> > log 0<br>>> -Infinity<br>> Oh. So... since when does Haskell know about infinity?<br>I should have mentioned that the underlying platform in my case is an
<br>Intel P4. Haskell does not specify a floating point implementation; the<br>assumption is that it uses whatever the platform provides because<br>anything else would be horribly inefficient. The P4 implements IEEE<br>
floating point, which as Andrew pointed out includes Infinity, -Infinity<br>and NaN as special cases of floating point values. It also seems to<br>distinguish between 0.0 and (-0.0), although they are still equal. For<br>
instance:<br><br>> (-0.0)<br>-0.0<br><br>> 1.0 / 0.0<br>Infinity<br><br>> 1.0 / (-0.0)<br>-Infinity<br><br>(Aside: should Infinity etc. be included in the Haskell standard? Or<br>possibly in a Data.Numeric.IEEE extension? They look like constructors,
<br>and it would be nice to be able to pattern match on them.)<br><br>So if you tried these experiments on a non-IEEE platform then you would<br>get different results. You might get exceptions or just wierd big<br>numbers. ISTR someone posted results from a Sun Sparc along these lines
<br>some time ago.<br>> foo x<br>> | x < 0 = ...<br>> | x == 0 = ...<br>> | x > 0 = ...<br>> I was most perplexed when I got a "non-exhaustive patterns"<br>> exception... It turns out there was a NaN in there. I forget about that.
<br>Nasty. I'll have to bear that one in mind myself.<br><br>You can detect infinities by equality, but not NaN.<br><br>> let inf = (1.0 / 0.0)<br>> let nan = inf * 0.0<br>> inf == inf<br>True<br>>nan == nan
<br>False<br><br>>> > exp (log 0 * 2)<br>>> 0.0<br>> Well that's interesting. I did wonder why it *doesn't* break in the<br>> real case...<br>I haven't perused the IEEE standard, but I expect it defines something
<br>like this:<br><br>Infinity * 0 = NaN<br>Infinity * _ = Infinity<br>exp Infinity = Infinity<br>exp (-Infinity) = 0<br>> Um... why would infinity * 0 be NaN? That doesn't make sense...<br>Infinity times anything is Infinity. Zero times anything is zero. So
<br>what should Infinity * zero be? There isn't one right answer. In this<br>case the "morally correct" answer is zero, but in other contexts it<br>might be Infinity or even some finite number other than zero.
<br><br>Consider 0.0 / 0.0, which also evaluates to NaN. What should its value<br>be? If you take the limit of (x / x) as x -> 0 then the right answer is<br>0. On the other hand if you take the limit of (0 / x) as x -> 0 then
<br>the right answer is infinity. Worse yet, if you take the limit of (2x /<br>x) as x-> 0 then the right answer is 2. You can pick any finite or<br>infinite value you like. So the only possible answer is NaN.<br>>
<br>>> So no, its not a bug, its according to the standard.<br>><br>> So I'm the only person who was expecting zero squared to be zero?<br>> (IMHO the standard should try to implement mathematical operations in
<br>> a mathematically sensible way...)<br>It does *try*. I'm not sure if IEEE arithmetic was actually defined<br>back in 98. It certainly wasn't widely implemented. There might well<br>be a case for revisiting the standard to allow for IEEE values, but you
<br>can't mandate them because not all platforms support IEEE arithmetic.<br>>> While working through this I also came across the following case<br>>> which technically is a bug:<br>>><br>>> > 0 ** 0
<br>>> 1.0<br>>><br>>> > exp (log 0 * 0)<br>>> NaN<br>>><br>>> I suspect that GHCi is using a built-in exponentiation operator that<br>>> doesn't quite conform to the standard in this case.
<br>><br>> Now that really *is* odd...<br>When I said "built in" I meant "built in to the hardware". This is<br>probably another special case defined in the IEEE standard, which is not<br>the same as the Haskell 98 definition.
<br><br>The reason why the IEEE standard was defined in the first place was that<br>floating point software portability was being seriously limited by these<br>corner cases. You would get some numerical code working on a Sun, and
<br>then find it broke on a PC because one of them defined (0.0 / 0.0) as 1<br>and the other defined it as 0. Worse yet, you might find it worked on<br>Intel chips but not IBM or AMD. Programmers also had to code explicit
<br>checks for division by zero and implement their own versions of Infinity<br>and NaN in cases where they might appear, which cluttered up the code<br>and slowed down execution.<br><br>One way out of this morass would be to define Haskell floating point
<br>arithmetic as IEEE standard, and document non-conformance for certain<br>platforms. In the long term that is probably the way forwards (do any<br>currently sold chips *not* implement IEEE?). It would also let us<br>
include Infinity and NaN as constructors. However it would lead to<br>significant problems when compiling code that used these values on<br>non-IEEE platforms. What do you do then?<br><br>Paul.<br><br>_______________________________________________
<br>Haskell-Cafe mailing list<br><a href="mailto:Haskell-Cafe@haskell.org">Haskell-Cafe@haskell.org</a><br><a href="http://www.haskell.org/mailman/listinfo/haskell-cafe">http://www.haskell.org/mailman/listinfo/haskell-cafe
</a><br></blockquote></div><br>