[Tutor] Fraction - differing interpretations for number and string

Steven D'Aprano steve at pearwood.info
Thu Apr 16 15:51:42 CEST 2015


On Thu, Apr 16, 2015 at 01:52:51AM -0700, Danny Yoo wrote:

> It's this last supposition that should be treated most seriously.  Most
> computers use "floating point", a representation of numbers that use a
> fixed set of bits.  This uniformity allows floating point math to be
> implemented quickly.  But it also means that it's inaccurate.  You're
> seeing evidence of the inaccuracy.

Hmmm. I wouldn't describe it as "inaccurate". The situation is a lot 
more subtle and complicated than mere inaccuracy, especially these days.

Back when dinosaurs ruled the earth, it would be accurate to describe 
floating point arithmetic as "inaccurate", pun intended. Some of the 
biggest companies in computing back in the 1970s had floating point 
arithmetic which was horrible. I'm aware of at least one system where 
code like this:

    if x != 0:
        print 1/x

could crash with a Divide By Zero error. And worse! But since IEEE-754 
floating point semantics has become almost universal, the situation is 
quite different. IEEE-754 guarantees that the four basic arithmetic 
operations + - * / will give the closest possible result to the exact 
mathematical result, depending on the rounding mode and available 
precision. If an IEEE-754 floating point system gives a result for some 
operation like 1/x, you can be sure that this result is the closest you 
can possibly get to the true mathematical result -- and is often exact.

The subtlety is that the numbers you type in decimal are not always the 
numbers the floating point system is actually dealing with, because they 
cannot be. What you get though, is the number closest possible to what 
you want.

Let me explain with an analogy. We all know that the decimal for 1/3 is 
0.33333-repeating, with an infinite number of threes after the decimal 
point. That means any decimal number you can possibly write down is not 
1/3 exactly, it will either be a tiny bit less, or a tiny bit more.

    0.333333333      # pretty close
    0.33333333333    # even closer
    0.3333333333333  # closer, but still not exact
    0.3333333333334  # too big

There is no way to write 1/3 exactly as a decimal, and no way to 
calculate it exactly as a decimal either. If you ask for 1/3 you will 
get something either a tiny bit smaller than 1/3 or a tiny bit bigger.

Computer floating point numbers generally use base 2, not base 10. That 
means that fractions like 1/2, 1/4, 1/8 and similar can be represented 
exactly (up to the limit of available precision) but many decimal 
numbers are like 1/3 in decimal and have an infinitely repeating binary 
form. Since we don't have an infinite amount of memory, we cannot 
represent them *exactly* as binary floats.

So the decimal fraction 0.5 means 5/10 or if you prefer, (5 * 1/10). 
That is the same as "1/2" or (1 * 1/2), which means that in base-2 we 
can write it as "0.1".

0.75 in decimal means (7 * 1/10 + 5 * 1/100). With a bit of simple 
arithmetic, you should be able to work out that 0.75 is also equal to 
(1/2 + 1/4), or to put it another way, (1 * 1/2 + 1 * 1/4) which can be 
written as "0.11" in base-2.

But the simple decimal number 0.1 cannot be written in an exact base-2 
form:

1/10 is smaller than 1/2, so base-2 "0.1" is too big;
1/10 is smaller than 1/4, so base-2 "0.01" is too big;
1/10 is smaller than 1/8, so base-2 "0.001" is too big;

1/10 is bigger than 1/16, so base-2 "0.0001" is too small;
1/10 is bigger than 1/16 + 1/32, so base-2 "0.00011" is too small;
1/10 is smaller than 1/16 + 1/32 + 1/64, so base-2 "0.000111" 
     is too big;

likewise base-2 "0.0001101" is too big (1/16 + 1/32 + 1/128);
base-2 "0.00011001" is too small (1/16 + 1/32 + 1/256);
and so on.

What we actually need is the infinitely repeating binary number:

0.00011001100110011001100110011...

where the 0011s repeat forever. But we cannot do that, since floats only 
have a fixed number of bits. We have to stop the process somewhere, and 
get something a tiny bit too small:

0.0001100110011001100110

or a tiny bit too big:

0.0001100110011001100111

depending on exactly how many bits we have available.

Is this "inaccurate"? Well, in the sense that it is not the exact true 
mathematical result, yes it is, but that term can be misleading if you 
think of it as "a mistake". In another sense, it's not inaccurate, it is 
as accurate as possible (given the limitation of only having a certain 
fixed number of bits). 



-- 
Steve


More information about the Tutor mailing list