pikapika
pikapika

Reputation: 376

Why are Float and Double different in the case of adding 0.1 and 0.2?

Can someone explain why

(0.1::Float) + (0.2::Float) == (0.3::Float)                                                                                                      

while

(0.1::Double) + (0.2::Double) /= (0.3::Double)

To my knowledge Double is supposed to be more precise. Is there something about Float I should know about?

Upvotes: 5

Views: 764

Answers (2)

alias
alias

Reputation: 30460

The first thing to realize is that when you enter 0.1::Double and ghci prints 0.1 back, it's only an "illusion:"

Prelude Data.Ratio> 0.1::Double
0.1

Why is that an illusion? Because the number 0.1 is actually not precisely representable as a floating point number! This is true for both Float and Double. Observe:

Prelude Data.Ratio> toRational (0.1::Float)
13421773 % 134217728
Prelude Data.Ratio> toRational (0.1::Double)
3602879701896397 % 36028797018963968

So, in reality, these numbers are indeed "close" to the actual real number 0.1, but neither is precisely 0.1. How close are they? Let's find out:

Prelude Data.Ratio> toRational (0.1::Float) - (1%10)
1 % 671088640
Prelude Data.Ratio> toRational (0.1::Double) - (1%10)
1 % 180143985094819840

As you see, Double is indeed a lot more precise than Float; the difference between the representation of 0.1 as a Double and the actual real-number 0.1 is a lot smaller. But neither is precise.

So, indeed the Double addition is a lot more precise, and should be preferred over the Float version. The confusing equality you see is nothing but the weird effect of rounding. The results of == should not be trusted in the floating-point land. In fact, there are many floating point numbers x such that x == x + 1 holds. Here's one example:

Prelude> let x = -2.1474836e9::Float
Prelude> x == x + 1
True

A good read on floating-point representation is the classic What Every Computer Scientist Should Know about Floating-Point Arithmetic, which explains many of these quirky aspects of floating-point arithmetic.

Also note that this behavior is not unique to Haskell. Any language that uses IEEE754 Floating-point arithmetic will behave this way, which is the standard implemented by modern microprocessors.

Upvotes: 16

dfeuer
dfeuer

Reputation: 48611

Double is indeed more precise. And yes, there is something you should know about floating point representations in general: you have to be very careful about how you use them! == is generally unlikely to actually be useful. You're generally going to want to compare floating point representations using more specialized functions, or at least check if the representation lies within some range rather than whether it has a certain value according to the built-in approximation.

Upvotes: 8

Related Questions