On Wed, Sep 01, 2010 at 04:46:30PM -0400, Jim wrote:
> Can anyone comment how they handle floating point precision situations  
> like this?
>
> Here is a basic toy program:
> #!/usr/bin/perl
>
> use strict;
>
> my $total = 0;
> my $a = 21835.30;
> my $b = -21715.90;
> my $c = 9652.20;
> my $d = -9771.60;
>
> print ("total = $total\n");
> $total += $a;
> print ("total = $total\n");
> $total += $b;
> print ("total = $total\n");
> $total += $c;
> print ("total = $total\n");
> $total += $d;
> print ("total = $total\n");
> $total = sprintf("%.2f", $total);
> print ("total = $total\n");
>
>
> And here is the output:
> total = 0
> total = 21835.3
> total = 119.399999999998
> total = 9771.6
> total = -1.81898940354586e-12
> total = -0.00
>
> Note the lack of precision adding these simple numbers which do not have  
> a large number of digits to the right of the decimal.
>
> If the line:
> $total = sprintf("%.2f", $total);
> would produce 0.00 rather than -0.00 I could live with this.
>
> But the only solution I have to get the final answer to properly be 0.00  
> is to format
> $total = sprintf("%.2f", $total);
> after each increment is added to $total
>
> or maybe put a string comparison at the end checking for "-0.00" and  
> changing it to "0.00" if it matches.
>
> I don't like either of these solutions.
>
> How do others deal with this?

Depending on exactly what you require, something like this might do what
you want:

#!/usr/bin/perl

use strict;

my $total = 0;
my $a = 21835.30;
my $b = -21715.90;
my $c = 9652.20;
my $d = -9771.60;

sub to2dp { sprintf "%.2f", $_[0] + 1e-8 }

print ("total = $total\n");
print  "to2dp = ", to2dp($total), "\n";
$total += $a;
print ("total = $total\n");
print  "to2dp = ", to2dp($total), "\n";
$total += $b;
print ("total = $total\n");
print  "to2dp = ", to2dp($total), "\n";
$total += $c;
print ("total = $total\n");
print  "to2dp = ", to2dp($total), "\n";
$total += $d;
print ("total = $total\n");
print  "to2dp = ", to2dp($total), "\n";
$total = sprintf("%.2f", $total);
print ("total = $total\n");
print  "to2dp = ", to2dp($total), "\n";

This produces the output:

total = 0
to2dp = 0.00
total = 21835.3
to2dp = 21835.30
total = 119.399999999998
to2dp = 119.40
total = 9771.6
to2dp = 9771.60
total = -1.81898940354586e-12
to2dp = 0.00
total = -0.00
to2dp = 0.00

When you want to compare floating point numbers, check that their
difference is less than some appropriately small delta:

$delta = 1e-8;

if (abs($a - $b) < $delta)  # numbers are "equal"


But ideally do it all in integers or use a special purpose library if
you need a greater range.  If you use floating point then ensure you
know its limits.

-- 
Paul Johnson - p...@pjcj.net
http://www.pjcj.net

-- 
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/


Reply via email to