Revert "Handle huge numbers better"

This reverts commit ef0039bc33.

Abysmal performance: revbank2beancount went from 0.7 to 11 seconds for
revspace's 2024 .revbank.log to date.
This commit is contained in:
Juerd Waalboer 2024-08-28 06:41:02 +02:00
parent 599bf1bc98
commit f16e406063
5 changed files with 41 additions and 54 deletions

View file

@ -8,7 +8,6 @@ use experimental 'signatures'; # stable since v5.36
use Carp qw(carp croak);
use Scalar::Util;
use POSIX qw(lround);
use Math::BigInt;
our $C = __PACKAGE__;
@ -42,12 +41,12 @@ use overload (
},
"*" => sub ($a, $b, $swap) {
$b = $b->_float_warn if ref $b;
$C->new($$a->as_float * ($b->can('float') ? $b->float : $b->can('as_float') ? $b->as_float : $b));
$C->new($$a * $b);
},
"/" => sub ($a, $b, $swap) {
carp "Using floating-point arithmetic for $a/$b (use \$amount->float to suppress warning)";
$b = $b->float if ref $b;
$C->new($$a->as_float / $b);
$C->new($$a / $b);
},
"<=>" => sub ($a, $b, $swap) {
_coerce($a, $b);
@ -60,15 +59,19 @@ use overload (
);
sub new($class, $cents) {
my $int = Math::BigInt->new($cents->can("as_int") ? $cents->as_int : $cents);
croak "Non-integer not supported" if $int->is_nan;
croak "Infinite number not supported" if $int->is_inf;
my $int = int sprintf "%d", $cents;
#carp "$cents rounded to $int cents" if $int != $cents;
return bless \$int, $class;
}
sub new_from_float($class, $num) {
return $class->new((100 * Math::BigFloat->new($num))->bfround(0)->as_int);
return $class->new(lround 100 * sprintf "%.02f", $num);
# First, round the float with sprintf for bankers rounding, then
# multiply to get number of cents. However, 4.56 as a float is
# 4.55999... which with int would get truncated to 455, so lround is needed
# to get 456.
# Note: _l_round, because normal round gives the int *AS A FLOAT*; sigh.
}
sub parse_string($class, $str) {
@ -85,7 +88,7 @@ sub parse_string($class, $str) {
$cents *= 10 if length($cents) == 1; # 4.2 -> 4.20
return $class->new(
($neg ? -1 : 1) * (100 * Math::BigInt->new($int) + $cents)
($neg ? -1 : 1) * ($int * 100 + $cents)
);
}
@ -94,7 +97,7 @@ sub cents($self) {
}
sub float($self) {
return $$self->as_float / 100;
return $$self / 100;
}
sub _float_warn($self) {
@ -104,9 +107,9 @@ sub _float_warn($self) {
sub string($self, $plus = "") {
return sprintf(
"%s%s.%02d",
"%s%d.%02d",
$$self < 0 ? "-" : $plus,
abs($$self) / 100, # %s for bigint
abs($$self) / 100,
abs($$self) % 100,
);
}
@ -116,7 +119,7 @@ sub string_flipped($self, $sep = " ") {
"%s%s%d.%02d",
$$self > 0 ? "+" : "",
$$self > 0 ? $sep : "",
abs($$self) / 100, # %s for bigint
abs($$self) / 100,
abs($$self) % 100,
);
}

View file

@ -14,32 +14,6 @@ use RevBank::FileIO;
sub reason($self) { return $$self; }
}
sub _parse_any_amount($amount) {
defined $amount or return undef;
length $amount or return undef;
my @split = grep /\S/, split /([+-])/, $amount;
my $posneg = 1;
$amount = RevBank::Amount->new(0);
for my $token (@split) {
if ($token eq '-') {
$posneg = $posneg == -1 ? 1 : -1;
} elsif ($token eq '+') {
$posneg ||= 1;
} else {
$posneg or return undef; # two terms in a row
my $term = RevBank::Amount->parse_string($token) // return undef;
$amount += $posneg * $term;
$posneg = 0;
}
}
$posneg and return undef; # last token must be term
return $amount;
}
sub import {
require RevBank::Plugins;
require RevBank::Users;
@ -57,9 +31,27 @@ sub import {
*{"$caller\::append"} = \&RevBank::FileIO::append;
*{"$caller\::with_lock"} = \&RevBank::FileIO::with_lock;
*{"$caller\::parse_user"} = \&RevBank::Users::parse_user;
*{"$caller\::parse_any_amount"} = \&_parse_any_amount;
*{"$caller\::parse_amount" } = sub ($amount) {
$amount = _parse_any_amount($amount) // return undef;
*{"$caller\::parse_amount"} = sub ($amount) {
defined $amount or return undef;
length $amount or return undef;
my @split = grep /\S/, split /([+-])/, $amount;
my $posneg = 1;
$amount = RevBank::Amount->new(0);
for my $token (@split) {
if ($token eq '-') {
$posneg = $posneg == -1 ? 1 : -1;
} elsif ($token eq '+') {
$posneg ||= 1;
} else {
$posneg or return undef; # two terms in a row
my $term = RevBank::Amount->parse_string($token) // return undef;
$amount += $posneg * $term;
$posneg = 0;
}
}
$posneg and return undef; # last token must be term
if ($amount->cents < 0) {
die RevBank::Exception::RejectInput->new(

View file

@ -21,9 +21,8 @@ sub username($self, $cart, $name, @) {
return REJECT, "Sorry, - + / ^ * are not allowed as the first character."
if $name =~ /^[-+*\/\^]/;
my $num;
return REJECT, "Sorry, that evaluates to the amount $num and can't be an account name."
if defined($num = parse_any_amount($name));
return REJECT, "Sorry, that's too numeric to be a user name."
if defined RevBank::Amount->parse_string($name);
return REJECT, "That name is not available."
if defined parse_user($name, 1);

View file

@ -17,7 +17,8 @@ use RevBank::Messages;
use RevBank::Cart;
use RevBank::Prompt;
our $VERSION = "6.2.3";
our $VERSION = "6.2.4";
our %HELP1 = (
"abort" => "Abort the current transaction",
);

View file

@ -40,12 +40,6 @@ 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));
@ -142,8 +136,6 @@ 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);