I have attached a patch to Math-BigInt-1.96 that fixes this bug. I have
also added a test suite entry that caches this case and verifies that it
works correctly.
diff -ru Math-BigInt-1.96//lib/Math/BigInt/Calc.pm Math-BigInt-1.96-patched//lib/Math/BigInt/Calc.pm
--- Math-BigInt-1.96//lib/Math/BigInt/Calc.pm 2010-09-28 06:34:18.000000000 +0200
+++ Math-BigInt-1.96-patched//lib/Math/BigInt/Calc.pm 2010-10-01 14:21:29.946547200 +0200
@@ -1206,20 +1206,18 @@
sub _digit
{
- # return the nth digit, negative values count backward
- # zero is rightmost, so _digit(123,0) will give 3
- my ($c,$x,$n) = @_;
+ # Return the nth digit. Zero is rightmost, so _digit(123,0) gives 3.
+ # Negative values count from the left, so _digit(123, -1) gives 1.
+ my ($class, $x, $n) = @_;
- my $len = _len('',$x);
+ my $len = _len('', $x);
- $n = $len+$n if $n < 0; # -1 last, -2 second-to-last
- $n = abs($n); # if negative was too big
- $len--; $n = $len if $n > $len; # n to big?
-
- my $elem = int($n / $BASE_LEN); # which array element
- my $digit = $n % $BASE_LEN; # which digit in this element
- $elem = '0' x $BASE_LEN . @$x[$elem]; # get element padded with 0's
- substr($elem,-$digit-1,1);
+ $n += $len if $n < 0; # -1 last, -2 second-to-last
+ return 0 if $n < 0 || $n >= $len;
+
+ my $elem = int($n / $BASE_LEN); # which array element
+ my $digit = $n % $BASE_LEN; # which digit in this element
+ substr("$x->[$elem]", -$digit-1, 1);
}
sub _zeros
diff -ru Math-BigInt-1.96//t/bigintc.t Math-BigInt-1.96-patched//t/bigintc.t
--- Math-BigInt-1.96//t/bigintc.t 2010-09-13 16:28:42.000000000 +0200
+++ Math-BigInt-1.96-patched//t/bigintc.t 2010-10-01 13:46:31.839617600 +0200
@@ -129,9 +129,11 @@
is ($C->_digit($x,0),9);
is ($C->_digit($x,1),8);
is ($C->_digit($x,2),7);
+is ($C->_digit($x,12),0);
is ($C->_digit($x,-1),1);
is ($C->_digit($x,-2),2);
is ($C->_digit($x,-3),3);
+is ($C->_digit($x,-12),0);
# _copy
foreach (qw/ 1 12 123 1234 12345 123456 1234567 12345678 123456789/)