tags:

views:

170

answers:

3

As part of a larger function definition, I needed to allow the domain (i, n) of a function to increment from i to n at varying rates. So I wrote:

f (i, n) k = [i, (i+k)..n]

into GHC. This returned odd results:

*Main> f (0.0, 1.0) 0.1
[0.0,0.1,0.2,0.30000000000000004,0.4000000000000001,0.5000000000000001,0.6000000000000001,0.7000000000000001,0.8,0.9,1.0]

Why does GHC return, e.g., 0.30000000000000004 instead of 0.3?

+8  A: 

Because IEEE floating point arithmetic can't express decimal numbers precisely, in general. There is always a rounding error in the binary representation, which sometimes seeps to the surface when displaying the number.

Depending on how GHC converts floating-point numbers to a decimal representation, you might find that on Windows it displays the result as the expected 0.3. This is because Microsoft's runtime libraries are smarter than Linux and Mac about how they present floats.

EDIT: This may not be the case. The number 0.3 will encode as the integer 3fd3333333333333 when using IEEE floats, whereas 0.1 + 0.1 + 0.1 will produce a number that encodes as 3fd3333333333334, and I don't know whether Microsoft's runtime libraries are tolerant enough to round back to 0.3 when displaying this.

In any event, a good example of the different handling is to type 0.3 into a Python interactive shell. If it's Python 2.6, you'll get back 0.29999999999999999, and if it's 2.7, it will display 0.3.

Marcelo Cantos
Interesting. Apparently I have a lot of wikipedia to read! Thanks.
danportin
@danportin See also "What Every Computer Scientist Should Know About Floating-Point Arithmetic" at http://docs.sun.com/source/806-3568/ncg_goldberg.html
Greg Bacon
+1  A: 

A better way of doing this is more along the lines of

map (/10) [0 .. 10]

This takes whole numbers, thereby avoiding the float problem, and divides each one by 10.

Paul Johnson
+1 (I wish it could be more) for a very wise observation.
Marcelo Cantos
Actually, it doesn't. Just because you aren't putting a decimal in the number doesn't mean it isn't one. So while the numbers may be "whole", you aren't avoiding the float problem. In fact, because of the way you've written it, you risk making the problem worse (because you're trying to convince yourself that the numbers aren't using floating-point).A better way of doing this would be to use `Rational`s (or some sort of `Ratio`-using type.
BMeph
The basic problem stems from the floating binary representation. 0.1 decimal is a recurring binary fraction, so you get rounding errors. However all integers up some very large value have precise representations as floats, so you are guaranteed not to get rounding errors as long as you stick to them. You can always specify IEE-488 arithmetic in the documentation if it makes you feel more comfortable.
Paul Johnson
+4  A: 
Greg Bacon
This answer is excellent. The above solution works better than expected (it solved some false Bool values from being reported in a few 'brute force this calc. problem' functions. The definition of 'printApprox' is over my head, but I don't mind reading Rationals (and, of course, the goal is that it /won't/ be over my head in a while).
danportin
@danportin Thanks. See updated answer.
Greg Bacon