Arbitrary decimal places in addition

M

Matthew Keene

I'm sure this question has been asked before, but I couldn't find
anything that really satisfactorily explained it, so please excuse me
if I'm going over old ground.

We have a program which totals decimal numbers in a file. The code in
the program looks something like this (I've added the print statements
to see what's going on):

use English ;

open (FILE,"DMD_WKG_FCT.TXT") or die "Couldn't open file" ;
while (defined($line = <FILE>)) {
chomp $line ;
@fields = split(/\t/,$line) ;
next if $INPUT_LINE_NUMBER == 1 ;
print "Input field = $fields[11] $sum\n" ;
}

print $sum ;

and the output that this produces looks like this (I've trimmed the
output to show the sections which illustrate the behaviour I'm talking
about)

Input field = 2.5237 Running total = 9864.4567
Input field = 0.8571 Running total = 9865.3138
Input field = 0.8571 Running total = 9866.1709
Input field = 15.6639 Running total = 9881.8348
Input field = 16.6392 Running total = 9898.47399999999
Input field = 6.6188 Running total = 9905.09279999999
Input field = 19.9114 Running total = 9925.0042
Input field = 0.0000 Running total = 9925.0042
Input field = 0.0000 Running total = 9925.0042
Input field = 0.0000 Running total = 9925.0042
Input field = 0.0000 Running total = 9925.0042
Input field = 0.0001 Running total = 9925.0043
Input field = 0.0001 Running total = 9925.00439999999
Input field = 0.0001 Running total = 9925.00449999999
Input field = 0.0001 Running total = 9925.00459999999
Input field = 4.0370 Running total = 9929.04159999999

Where did the extra decimal places come from ?

Now, we could probably solve this by rounding all numbers to 4 decimal
places, but the problem is that this is a utility program which is
meant to be able to handle an arbitrary number of decimal places, so
we wouldn't necessary know how many decimal places to round to (I
guess we could find the greatest number of decimal places in the input
and round to that, but this seems to be getting a bit silly).

Can anybody explain this behaviour and the best way to get around it ?
 
C

Chris Mattern

Matthew said:
I'm sure this question has been asked before, but I couldn't find
anything that really satisfactorily explained it, so please excuse me
if I'm going over old ground.

We have a program which totals decimal numbers in a file. The code in
the program looks something like this (I've added the print statements
to see what's going on):

use English ;

open (FILE,"DMD_WKG_FCT.TXT") or die "Couldn't open file" ;
while (defined($line = <FILE>)) {
chomp $line ;
@fields = split(/\t/,$line) ;
next if $INPUT_LINE_NUMBER == 1 ;
print "Input field = $fields[11] $sum\n" ;
}

print $sum ;

and the output that this produces looks like this (I've trimmed the
output to show the sections which illustrate the behaviour I'm talking
about)

Input field = 2.5237 Running total = 9864.4567
Input field = 0.8571 Running total = 9865.3138
Input field = 0.8571 Running total = 9866.1709
Input field = 15.6639 Running total = 9881.8348
Input field = 16.6392 Running total = 9898.47399999999
Input field = 6.6188 Running total = 9905.09279999999
Input field = 19.9114 Running total = 9925.0042
Input field = 0.0000 Running total = 9925.0042
Input field = 0.0000 Running total = 9925.0042
Input field = 0.0000 Running total = 9925.0042
Input field = 0.0000 Running total = 9925.0042
Input field = 0.0001 Running total = 9925.0043
Input field = 0.0001 Running total = 9925.00439999999
Input field = 0.0001 Running total = 9925.00449999999
Input field = 0.0001 Running total = 9925.00459999999
Input field = 4.0370 Running total = 9929.04159999999

Where did the extra decimal places come from ?

Rounding errors. The computer is not doing the arithmetic in decimal,
and so cannot always represent your decimal fractions with complete
accuracy. It must often round when converting your input. In particular,
0.0001 is a non-terminating decimal in binary floating point.
Sometimes the rounding errors show up in your output. Among other things,
this results in the First Commandment of Floating Point: "Thou shalt
not compare two floats for equality," since you can't count on the
rounding errors having made them unequal when they shouldn't be.
Now, we could probably solve this by rounding all numbers to 4 decimal
places, but the problem is that this is a utility program which is
meant to be able to handle an arbitrary number of decimal places, so
we wouldn't necessary know how many decimal places to round to (I
guess we could find the greatest number of decimal places in the input
and round to that, but this seems to be getting a bit silly).

Can anybody explain this behaviour and the best way to get around it ?

Basically, if you must have arbitrary precision, you're going to have to
go out and get it; look at the Math::BigFloat package that's standard.
Note that you *still* have to know what precision and accuracy you want.

--
Christopher Mattern

"Which one you figure tracked us?"
"The ugly one, sir."
"...Could you be more specific?"
 
J

Jürgen Exner

Matthew Keene wrote:
[...]
Input field = 0.0001 Running total = 9925.00449999999
Where did the extra decimal places come from ?
[...]

Your Question is Asked Frequently, please see "perldoc -q 9999" for the
answer.

jue
 
B

Bill Smith

--snip
guess we could find the greatest number of decimal places in the input
and round to that, but this seems to be getting a bit silly).

If you want the input format to control the output format, yes, you do
have to do it explicitly.

This is not a limitation of perl, but rather of binary floating point
arithmetic. Nearly all computers do arithmetic with some variation of
this. All general purpose languages such as perl use the computer's
arithmethic instructions.

Bill
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top