}
sub round_amount {
- $main::lxdebug->enter_sub(2);
-
my ($self, $amount, $places) = @_;
- my $round_amount;
# Rounding like "Kaufmannsrunden" (see http://de.wikipedia.org/wiki/Rundung )
# Round amounts to eight places before rounding to the requested
# number of places. This gets rid of errors due to internal floating
# point representation.
- $amount = $self->round_amount($amount, 8) if $places < 8;
- $amount = $amount * (10**($places));
- $round_amount = int($amount + .5 * ($amount <=> 0)) / (10**($places));
+ $amount = $self->round_amount($amount, 8) if $places < 8;
- $main::lxdebug->leave_sub(2);
+ # Remember the amount's sign but calculate in positive values only.
+ my $sign = $amount <=> 0;
+ $amount = abs $amount;
- return $round_amount;
+ # Shift the amount left by $places+1 decimal places and truncate it
+ # to integer. Then to the integer equivalent of rounding to the next
+ # multiple of 10: first add half of it (5). Then truncate it back to
+ # the lower multiple of 10 by subtracting $amount modulo 10.
+ my $shift = 10 ** ($places + 1);
+ $amount = int($amount * $shift) + 5;
+ $amount -= $amount % 10;
+ # Lastly shift the amount back right by $places+1 decimal places and
+ # restore its sign. Then we're done.
+ $amount = ($amount / $shift) * $sign;
+
+ return $amount;
}
sub parse_template {
--- /dev/null
+use strict;
+use Test::More;
+
+use lib 't';
+use Support::TestSetup;
+
+Support::TestSetup::login();
+
+my $config = {};
+
+$config->{numberformat} = '1.000,00';
+
+# Positive values
+is($::form->round_amount(1.05, 2), '1.05', '1.05 @ 2');
+is($::form->round_amount(1.05, 1), '1.1', '1.05 @ 1');
+is($::form->round_amount(1.05, 0), '1', '1.05 @ 0');
+
+is($::form->round_amount(1.045, 2), '1.05', '1.045 @ 2');
+is($::form->round_amount(1.045, 1), '1', '1.045 @ 1');
+is($::form->round_amount(1.045, 0), '1', '1.045 @ 0');
+
+is($::form->round_amount(33.675, 2), '33.68', '33.675 @ 2');
+is($::form->round_amount(33.675, 1), '33.7', '33.675 @ 1');
+is($::form->round_amount(33.675, 0), '34', '33.675 @ 0');
+
+is($::form->round_amount(44.9 * 0.75, 2), '33.68', '44.9 * 0.75 @ 2');
+is($::form->round_amount(44.9 * 0.75, 1), '33.7', '44.9 * 0.75 @ 1');
+is($::form->round_amount(44.9 * 0.75, 0), '34', '44.9 * 0.75 @ 0');
+
+is($::form->round_amount(149.175, 2), '149.18', '149.175 @ 2');
+is($::form->round_amount(149.175, 1), '149.2', '149.175 @ 1');
+is($::form->round_amount(149.175, 0), '149', '149.175 @ 0');
+
+is($::form->round_amount(198.90 * 0.75, 2), '149.18', '198.90 * 0.75 @ 2');
+is($::form->round_amount(198.90 * 0.75, 1), '149.2', '198.90 * 0.75 @ 1');
+is($::form->round_amount(198.90 * 0.75, 0), '149', '198.90 * 0.75 @ 0');
+
+# Negative values
+is($::form->round_amount(-1.05, 2), '-1.05', '-1.05 @ 2');
+is($::form->round_amount(-1.05, 1), '-1.1', '-1.05 @ 1');
+is($::form->round_amount(-1.05, 0), '-1', '-1.05 @ 0');
+
+is($::form->round_amount(-1.045, 2), '-1.05', '-1.045 @ 2');
+is($::form->round_amount(-1.045, 1), '-1', '-1.045 @ 1');
+is($::form->round_amount(-1.045, 0), '-1', '-1.045 @ 0');
+
+is($::form->round_amount(-33.675, 2), '-33.68', '33.675 @ 2');
+is($::form->round_amount(-33.675, 1), '-33.7', '33.675 @ 1');
+is($::form->round_amount(-33.675, 0), '-34', '33.675 @ 0');
+
+is($::form->round_amount(-44.9 * 0.75, 2), '-33.68', '-44.9 * 0.75 @ 2');
+is($::form->round_amount(-44.9 * 0.75, 1), '-33.7', '-44.9 * 0.75 @ 1');
+is($::form->round_amount(-44.9 * 0.75, 0), '-34', '-44.9 * 0.75 @ 0');
+
+is($::form->round_amount(-149.175, 2), '-149.18', '-149.175 @ 2');
+is($::form->round_amount(-149.175, 1), '-149.2', '-149.175 @ 1');
+is($::form->round_amount(-149.175, 0), '-149', '-149.175 @ 0');
+
+is($::form->round_amount(-198.90 * 0.75, 2), '-149.18', '-198.90 * 0.75 @ 2');
+is($::form->round_amount(-198.90 * 0.75, 1), '-149.2', '-198.90 * 0.75 @ 1');
+is($::form->round_amount(-198.90 * 0.75, 0), '-149', '-198.90 * 0.75 @ 0');
+
+done_testing;
+
+1;