Handle huge numbers better

A sufficiently big number, i.e. longer than a long long, had interesting
effects. Perl would promote it to a float, and format it as -1 in
sprintf, which RevBank::Amount didn't handle correctly. In extreme cases
the number got rounded to Inf and would no longer round-trip.

As a result, numbers returned by RevBank::Amount are now Math::BigInt
and Math::BigFloat objects. Those should be transparent to all existing
code. It's amazing to see the unit tests pass.

I don't think there is any actual use case in RevBank for numbers this
large and I don't think anyone will have actually encountered the
aforementioned weird effects. Mostly, the input would be parsed with
parse_amount which refuses any number greater than 99900 anyway. Only
where parse_string was used directly, such large numbers could actually
have been used, but in stock RevBank that is only done when reading the
accounts file.

This change also introduces a new global function parse_any_amount that
is like parse_amount but doesn't complain about negative or large
numbers, to further improve the adduser plugin (see previous commit) in
insane edge cases. It differs from RevBank::Amount->parse_string in that
it does support addition and subtraction operators.
This commit is contained in:
Juerd Waalboer 2024-08-28 05:08:20 +02:00
parent f6f5d66bdc
commit ef0039bc33
5 changed files with 54 additions and 40 deletions

View file

@ -40,6 +40,12 @@ cmp_ok(RevBank::Amount->parse_string("4 ")->cents, '==', 400);
cmp_ok(RevBank::Amount->new_from_float(.425)->cents, '==', 42);
my $big = "9" x 123;
cmp_ok(RevBank::Amount->new($big)->cents, 'eq', $big);
cmp_ok(RevBank::Amount->parse_string($big)->cents, 'eq', $big . "00");
cmp_ok(RevBank::Amount->new_from_float($big)->cents, 'eq', $big . "00");
# comparisons
ok($a);
ok(!RevBank::Amount->new(0));
@ -136,6 +142,8 @@ like(warning { rand $a }, qr/float/);
# Invalid amounts
throws_ok(sub { RevBank::Amount->new(1.1) }, qr/Non-integer/);
throws_ok(sub { RevBank::Amount->new("+Inf") }, qr/Infinite/);
is(RevBank::Amount->parse_string("0.000"), undef);
is(RevBank::Amount->parse_string("0.042"), undef);
is(RevBank::Amount->parse_string("+0.042"), undef);