MiniVend Akopia Services

[Date Prev][Date Next][Thread Prev][Thread Next][Minivend by date ][Minivend by thread ]

Rounding solution




>From the Perl FAQ:

    Rounding in financial applications can have serious
    implications, and the rounding method used should be specified
    precisely. In these cases, it probably pays not to trust
    whichever system rounding is being used by Perl, but to instead
    implement the rounding function you need yourself.

    To see why, notice how you'll still have an issue on half-way-
    point alternation:

        for ($i = 0; $i < 1.01; $i += 0.05) { printf "%.1f ",$i}

        0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7 
        0.8 0.8 0.9 0.9 1.0 1.0

    Don't blame Perl. It's the same as in C. IEEE says we have to do
    this. Perl numbers whose absolute values are integers under
    2**31 (on 32 bit machines) will work pretty much like
    mathematical integers. Other numbers are not guaranteed.

Aha, I always like a blessing to do something myself:

sub round_to_frac_digits {
    my ($num, $digits) = @_;
    if (defined $digits) {
        # use what we were given
    }
    elsif ( defined $Vend::Cfg->{Locale} ) {
        $digits = $Vend::Cfg->{Locale}{frac_digits} || 2;
    }
    else {
        $digits = 2;
    }
    my @frac;
    $num =~ /^(\d*)\.(\d+)$/
        or return $num;
    my $int = $1; 
    @frac = split //, $2; 
	local($^W) = 0;
    my $frac = join "", @frac[0 .. $digits - 1];
    if($frac[$digits] > 4) {
        $frac++;
    }
    if(length($frac) > $digits) {
        $int++;
        $frac = 0 x $digits;
    }
    return "$int.$frac";
}

my ($i, $r, $f);

for ($i = 0; $i < 1.01; $i += 0.005) {
	$r = round_to_frac_digits($i);
	$r = sprintf("%.2f", $r);
	$f = sprintf("%.2f", $i);
	next if $r eq $f;
	print "$i printf=$f round_to_frac_digits=$r\n";
}

!2{perl -Mstrict -w

0.015 printf=0.01 round_to_frac_digits=0.02
0.045 printf=0.04 round_to_frac_digits=0.05
0.055 printf=0.05 round_to_frac_digits=0.06
0.065 printf=0.06 round_to_frac_digits=0.07
0.075 printf=0.07 round_to_frac_digits=0.08

So the solution is to place that routine somewhere (I will pick Vend::Util
with it in @EXPORT_OK) and then

	$r = round_to_frac_digits($r);

-- 
Mike Heins                          http://www.minivend.com/  ___ 
                                    Internet Robotics        |_ _|____
                                    131 Willow Lane, Floor 2  | ||  _ \
It's a little-known fact            Oxford, OH  45056         | || |_) |
that the Y1K problem caused         <mikeh@minivend.com>     |___|  _ <
the Dark Ages. -- unknown           513.523.7621 FAX 7501        |_| \_\


Search for: Match: Format: Sort by: