Re: Proper monetary rounding
by izut (Chaplain) on Mar 04, 2007 at 22:35 UTC
|
You should use require instead of use if you want lazy importing. Check perldoc -f use and perldoc -f require.
Igor 'izut' Sutton
your code, your rules.
| [reply] [d/l] [select] |
|
| [reply] |
Re: Proper monetary rounding
by merlyn (Sage) on Mar 05, 2007 at 00:46 UTC
|
The built-in sprintf implements round-to-even already:
for (qw(1.0049 1.0050 1.0051 1.0149 1.0150 1.0151 1.0249 1.0250 1.0251
+)) {
printf "%s => %0.2f\n", $_, $_;
}
...
1.0049 => 1.00
1.0050 => 1.00
1.0051 => 1.01
1.0149 => 1.01
1.0150 => 1.01
1.0151 => 1.02
1.0249 => 1.02
1.0250 => 1.02
1.0251 => 1.03
Hmm. Why didn't that work? {grin}
| [reply] [d/l] |
|
That's what I had been doing, until I heard that accountants are actually picky about this sort of thing.
"Round to even" considers the case where the number just past the least significant is a 5, followed only by zeroes. It then rounds odd LSDs up to the nearest even, rounding off even LSDs.
| [reply] |
|
perl -e 'my $x = 1.5050; my $y = 1.5150; my $s = sprintf "%0.2f\n%0.2f
+\n", $x, $y; print $s;'
1.50
1.52
"Normal" (gradeschool) rounding would produce 1.51 and 1.52 there, but
sprintf says 1.50 and 1.52, which seems an aweful lot like round-to-even
to me. How does it differ from what you wanted?
As far as accountants being picky about this... have you ever seen
the movie Superman 3?
| [reply] [d/l] |
|
|
|
Re: Proper monetary rounding
by blogical (Pilgrim) on Mar 05, 2007 at 15:59 UTC
|
Here's a little toy to examine just what's going on here. Try it out and play with the ranges.
#!/usr/bin/perl -w
use strict;
require Math::BigFloat;
my $sig = $ARGV[0] || 2;
my $ini = $ARGV[1] || 0;
my $end = $ARGV[2] || 1;
my $inc = $ARGV[3] || 0.0001;
test_methods($sig, $ini, $end, $inc);
sub test_methods {
my ($sig, $ini, $end, $inc) = @_;
my $count = 0;
my %result = ();
print "For $ini to $end, incrementing by $inc, with $sig significa
+nt digit". ($sig==1 ? '' : 's') ."\n";
for (my $num = $ini;$num<$end;$num += $inc) {
$count++;
$result{'real'} += $num;
$result{'ffround'} += Math::BigFloat->new($num)->ffround( -$si
+g );
$result{'sprintf'} += sprintf "%0.${sig}f", $num;
printf "%-20s || %-10s || %-20s\r", ($result{'real'}/$count),
+($result{'ffround'}/$count), ($result{'sprintf'}/$count);
}
print ' 'x58 . "\rOver $count iterations:\n";
print " Method | Sum | Average\n";
for (qw[real ffround sprintf]) {
printf "%9s | %-20s| %-20s\n", $_, $result{$_}, ($result{$_}/$
+count);
}
return \%result;
}
"One is enough. If you are acquainted with the principle, what do you care for the myriad instances and applications?" - Henry David Thoreau, Walden
| [reply] [d/l] |
Re: Proper monetary rounding
by ysth (Canon) on Mar 07, 2007 at 12:40 UTC
|
Round to even doesn't work so well on floating point, unless you are rounding to an integer. This is because it is supposed to do round to closest unless the amount
rounded off is 1/2th, 1/20th, 1/200th, 1/2000th, etc. (depending on what decimal
place you are rounding to), and of those, only 1/2 is representable in floating
point. None of the others will necessarily exactly occur, so the special round-to-even rule is not always actually invoked.
What you really ought to do is pick an epsilon that represents how much off
your floating point number may be from what the actual infinitely precise value
would have been - taking into account both the inaccuracy of floating point representation and any round-off errors involved in calculations that led to
the number in question. Given the latter factor, there is no one epsilon that
will suit any problem. Then you apply the round-to-even rule if your number
is within epsilon of 1/20th, 1/200th, etc.
I have the suspicion that the ffround is working because at some point the
number is converted to string form, and then to a bigfloat internal form,
and the conversion to string does a slight amount of rounding for you. If
so, it won't necessarily work 100% of the time.
| [reply] |
Re: Proper monetary rounding
by Moron (Curate) on Mar 07, 2007 at 14:07 UTC
|
The problem is that if there is any validity in the argument about trending upward, all this does it make it trend downward, which is not so clever if you are invoicing rather than paying out :).
In fact either kind of trending is a bug that comes from rounding when you shouldn't - instead, maintain maximum possible precision throughout the lifetime of your data and only round for the purposes of display and reporting alone - then either type of rounding won't be cumulative (i.e. "trend-forming").
| [reply] |
|
Unless it's a lattitudinal trend across a large number of figures being calculated at the same time, all being derived from the same initial figure using the same formula, the sum of which consistantly exceeds the original figure due to a bias in the method of calculation.
The point of the round to even method, if properly implemented, is that it recognizes the situations where error is likely to occur and neutralizes the tendancy to err in either direction.
What you said about avoiding rounding- ie transformation of the source figure- is true. But it isn't necessarily a longitudinal problem.
| [reply] |
|
Bias has a continuous possibly increasing relationship with the input which has definite mean effect and is apt to have a Poisson distribution whereas rounding is discrete (only renders an effect at regular intervals) and does not render a mean overall effect. The probability of it being feasible to control one with the other is therefore zero. A better and tried and tested idea would be either feed back or feed forward a predictive or measured negation of the bias.
| [reply] |