Skip to content
Permalink
Browse files

Fix for FatRat.base failure for large denominators

FatRat.base conversion with denominators larger than 2**1023 (~10**308) falls into an infinite process sequence calculation. The problem is in the `$!denominator.log($base).ceiling + 1` code on line 213. log of integers > 10*308 or so returns Inf, and it tries to calculate the fraction to infinite precision. This pull request works around that and has a few other minor speed-up tweaks. Spectest has some failures but they aren't related to FatRats, base conversion or stringification so I think this is ok to merge. See https://www.nntp.perl.org/group/perl.perl6.users/2019/03/msg6640.html for bug report.
  • Loading branch information...
thundergnat committed Mar 27, 2019
1 parent 8fa3360 commit d4e1d8e7ee727ee2ddf1632752146c623a3411c8
Showing with 22 additions and 12 deletions.
  1. +22 −12 src/core/Rational.pm6
@@ -189,6 +189,13 @@ my role Rational[::NuT = Int, ::DeT = ::("NuT")] does Real {
2 <= $base <= 36 or fail X::OutOfRange.new(
what => "base argument to base", :got($base), :range<2..36>);

my $sign = nqp::if( nqp::islt_I($!numerator, 0), '-', '' );
my $whole = self.abs.floor;
my $fract = self.abs - $whole;

# fight floating point noise issues RT#126016
if $fract.Num == 1e0 { $whole++; $fract = 0 }

my $prec;
if $digits ~~ Whatever {
$digits = Nil;
@@ -210,16 +217,18 @@ my role Rational[::NuT = Int, ::DeT = ::("NuT")] does Real {
}
}
else {
$prec = ($!denominator < $base**6 ?? 6 !! $!denominator.log($base).ceiling + 1);
my $lim = 10**307;
if $!denominator < $lim {
$prec = ($!denominator < $base**6 ?? 6 !! $!denominator.log($base).ceiling + 1);
}
else {
my $f = $!denominator;
my $exp2 = 0;
++$exp2 while ($f div= $base) > $lim;
$prec = $exp2 + $f.log($base).ceiling + 2;
}
}

my $sign = nqp::if( nqp::islt_I($!numerator, 0), '-', '' );
my $whole = self.abs.floor;
my $fract = self.abs - $whole;

# fight floating point noise issues RT#126016
if $fract.Num == 1e0 { $whole++; $fract = 0 }

my $result = $sign ~ $whole.base($base);
my @conversion := <0 1 2 3 4 5 6 7 8 9
A B C D E F G H I J
@@ -236,16 +245,17 @@ my role Rational[::NuT = Int, ::DeT = ::("NuT")] does Real {

# Round the final number, based on the remaining fractional part
if 2*$fract >= 1 {
for @fract-digits-1 ... 0 -> $n {
for @fract-digits - 1 ... 0 -> $n {
last if ++@fract-digits[$n] < $base;
@fract-digits[$n] = 0;
$result = $sign ~ ($whole+1).base($base) if $n == 0;
}
}

@fract-digits
?? $result ~ '.' ~ @conversion[@fract-digits].join
!! $result;
$result ~
(@fract-digits ??
$base <= 10 ?? '.' ~ @fract-digits.join !!
'.' ~ @conversion[@fract-digits].join !! '')
}

method base-repeating($base = 10) {

0 comments on commit d4e1d8e

Please sign in to comment.
You can’t perform that action at this time.