Pathologically Eclectic Rubbish Lister PerlMonks

by vptimmy (Initiate)
 on Dec 01, 2011 at 21:59 UTC Need Help??
vptimmy has asked for the wisdom of the Perl Monks concerning the following question:

I have been banging my head against the wall and thought i should find some help. This is some really basic addition and subtraction that should equal 0.

#!/usr/bin/perl use strict; my \$first_num = '-3500.78'; my \$second_num = '-1057.12'; my \$third_num = '5100'; my \$fourth_num = '-681'; my \$fifth_num = '46.3'; my \$sixth_num = '46.3'; my \$seventh_num = '46.3'; print "FIRST NUMBER = \$first_num\n"; print "SECOND NUMBER = \$second_num\n"; print "THIRD NUMBER = \$third_num\n"; print "FOURTH NUMBER = \$fourth_num\n"; print "FIFTH NUMBER = \$fifth_num\n"; print "SIXTH NUMBER = \$sixth_num\n"; print "SEVENTH NUMBER = \$seventh_num\n"; my \$total = \$first_num + \$second_num + \$third_num + \$fourth_num + \$fif +th_num + \$sixth_num + \$seventh_num; print "TOTAL = \$total\n\n"; #BREAK IT DOWN my \$first_total = \$first_num + \$second_num; print "#1: \$first_num + \$second_num = \$first_total\n"; my \$second_total = \$first_total + \$third_num; print "#2: \$first_total + \$third_num = \$second_total\n"; my \$third_total = \$second_total + \$fourth_num; print "#3: \$second_total + \$fourth_num = \$third_total\n"; my \$fourth_total = \$third_total + \$fifth_num; print "#4: \$third_total + \$fifth_num = \$fourth_total\n"; my \$fifth_total = \$fourth_total + \$sixth_num; print "#5: \$fourth_total - \$sixth_num = \$fifth_total\n"; my \$sixth_total = \$fifth_total + \$seventh_num; print "#6: \$fifth_total + \$seventh_num = \$sixth_total\n";

Here are the results of the program:

FIRST NUMBER = -3500.78
SECOND NUMBER = -1057.12
THIRD NUMBER = 5100
FOURTH NUMBER = -681
FIFTH NUMBER = 46.3
SIXTH NUMBER = 46.3
SEVENTH NUMBER = 46.3
TOTAL = 3.5527136788005e-13

#1: -3500.78 + -1057.12 = -4557.9 (correct)
#2: -4557.9 + 5100 = 542.1 (correct)
#3: 542.1 + -681 = -138.9 (correct)
#4: -138.9 + 46.3 = -92.5999999999996 (WHAT?)
#5: -92.5999999999996 - 46.3 = -46.2999999999996 (WHAT?)
#6: -46.2999999999996 + 46.3 = 3.5527136788005e-13 (WHAT?)

I have a program that goes out and adds a budget up and kept encountering this problem. I decided to simplify it and break it down to try to find where the issue is. Been a long day and think my brains are just leaking out of my head. Thanks in advance, -vptimmy

Replies are listed 'Best First'.
by chromatic (Archbishop) on Dec 01, 2011 at 22:10 UTC

This is never obvious until you encounter it, but computers represent floating-point numbers in an interesting way. perlfaq4's "Why am I getting long decimals (eg, 19.9499999999999) instead of the numbers I should be getting (eg, 19.95)?" says:

Internally, your computer represents floating-point numbers in binary. Digital (as in powers of two) computers cannot store all numbers exactly. Some real numbers lose precision in the process. This is a problem with how computers store numbers and affects all computer languages, not just Perl.

It also points to the PDF What Every Computer Scientist Should Know About Floating-Point Arithmetic.

One of the easiest solutions is to store monetary values as pennies (or whatever the fractional amount of your local currency is) to avoid floating point values altogether.

(As a side note, if you're declaring numeric values, you're better off writing my \$first_num = -3500.78; without the quotes; there's no need to make them string values if you're going to treat them as numerics.)

Improve your skills with Modern Perl: the free book.

by toolic (Bishop) on Dec 01, 2011 at 22:09 UTC
by GrandFather (Sage) on Dec 01, 2011 at 22:15 UTC

If you are dealing with a budget work in cents and the issue will go away. The problem is that most decimal fractions can't be represented as finite binary fractions and computers tend to have finite representations for numbers.

There are various other ways to deal with the problem depending on your application. But if you are dealing with money to the nearest cent then working in cents avoids the problem entirely because you are then working with integers which are exactly represented in binary or decimal.

True laziness is hard work
Don't make the assumption every currency has cents. Some currencies use mills, some don't have subunits, some don't have mills, but they do have mill prices (pumped gas lately?), and some (digital only) currencies use 5 digits after the decimal point.

Even with 5 decimal digits, a 64-bit int can represent 184 trillion, which in dollars is roughly 3 times the entire worlds gross domestic product for 2010. Should be good enough for the average Internet reseller for a few years.

With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

The start of some sanity?

by choroba (Chancellor) on Dec 01, 2011 at 22:13 UTC
by JavaFan (Canon) on Dec 01, 2011 at 22:09 UTC
Most floating point numbers cannot be represented by a native float. You're suffering from rounding errors.
by ikegami (Pope) on Dec 01, 2011 at 23:18 UTC
78/100, 12/100 and 3/10 are all periodic numbers in binary (like 1/3 is in decimal). As such, they cannot be accurately represented by floating point numbers.
by InfiniteSilence (Curate) on Dec 08, 2011 at 01:01 UTC
perl -e 'use Math::BigFloat; my \$x = Math::BigFloat->new(q|-138.9|); p +rint \$x->badd(q|46.3|);'
Returns
-92.6

Celebrate Intellectual Diversity

Create A New User
Node Status?
node history
Node Type: perlquestion [id://941175]
Approved by toolic
Front-paged by Corion
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (8)
As of 2017-08-18 09:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
Who is your favorite scientist and why?

Results (298 votes). Check out past polls.

Notices?