Floating Point Rounding Errors

In Chapter 3 of my book, I mentioned offhand that sometimes you expect the number 5, but you get 4.99999999998 instead. I sort of punted on the explanation because it seemed to be a touch of a distraction. Naturally, chromatic called me on that and suggested I explain a bit more. As part of my explanation, I wrote a sample program that would print out the fractions used to build the mantissa of a number. For example, .75 is 1/2 + 1/4.

Here's the program:

use strict;                                                                                                                                                                    
use warnings;

my $num  = .75;
my $bits = 32;

my $accumulator = 0;
my $bitstring   = '';
my @fractions;
for ( 1 .. $bits ) {
    my $denominator = 2**$_;
    my $fraction    = 1 / $denominator;
    if ( $accumulator + $fraction <= $num ) {
        push @fractions, "1/$denominator";
        $bitstring .= "1";
        $accumulator += $fraction;
    }
    else {
        $bitstring .= "0";
    }
}
my $fractions = join " + ", @fractions;
print <<"END";
Fractions: $fractions
Bits:      $bitstring
Result:    $accumulator
END

So running that will print:

Fractions: 1/2 + 1/4
Bits:      11000000000000000000000000000000
Result:    0.75

But using .3 for the number will print (formatted to fit the blog):

Fractions: 1/4 + 1/32 + 1/64 + 1/512 + 1/1024 + 1/8192 
  + 1/16384 + 1/131072 + 1/262144 + 1/2097152 
  + 1/4194304 + 1/33554432 + 1/67108864
  + 1/536870912 + 1/1073741824
Bits:      01001100110011001100110011001100
Result:    0.299999999813735

I think this plus my accompanying text (not reproduced here) does a reasonable job of showing the rounding errors. Can I do better?

2 Comments

I think this is a great way to visualize how a limited number of bits are mangled into representing something that can't be accurately enumerated that way.

That's a great idea and a great program to do it.

The only suggestion I have is that you accept the number from the command-line: to make it easier for people to run it with different values and see how the results change.

Leave a comment

About Ovid

user-pic Have Perl; Will Travel. Freelance Perl/Testing/Agile consultant. Photo by http://www.circle23.com/. Warning: that site is not safe for work. The photographer is a good friend of mine, though, and it's appropriate to credit his work.