Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions mrbgems/mruby-bigint/core/bigint.c
Original file line number Diff line number Diff line change
Expand Up @@ -4091,20 +4091,24 @@ mpz_get_int(mpz_t *y, mrb_int *v)
return TRUE;
}

/* The negative range is one wider than the positive one, so MRB_INT_MIN
fits as an absolute value of MRB_INT_MAX + 1. */
mrb_uint limit = (mrb_uint)MRB_INT_MAX + (y->sn < 0 ? 1 : 0);

#ifdef MRB_NO_MPZ64BIT
/* When using 16-bit limbs, we need to handle larger accumulation */
mrb_uint i = 0;
mp_limb *d = y->p + y->sz;

while (d-- > y->p) {
/* Check for overflow before shifting */
if (i > (mrb_uint)(MRB_INT_MAX >> DIG_SIZE)) {
if (i > (limit >> DIG_SIZE)) {
return FALSE;
}
i = (i << DIG_SIZE) | *d;
}

if (i > (mrb_uint)MRB_INT_MAX) {
if (i > limit) {
return FALSE;
}
#else
Expand All @@ -4119,14 +4123,16 @@ mpz_get_int(mpz_t *y, mrb_int *v)
}
i = (i << DIG_SIZE) | *d;
}
if (i > MRB_INT_MAX) {
if (i > limit) {
/* overflow */
return FALSE;
}
#endif

if (y->sn < 0) {
*v = -(mrb_int)i;
/* On this branch `limit` is the absolute value of MRB_INT_MIN, which has
no positive counterpart to negate, so it is spelled out instead. */
*v = (i == limit) ? MRB_INT_MIN : -(mrb_int)i;
}
else {
*v = (mrb_int)i;
Expand Down
19 changes: 19 additions & 0 deletions mrbgems/mruby-bigint/test/bigint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@
end
end

assert 'Bigint normalizes the smallest Integer' do
# An Integer's negative range is one wider than its positive one: where
# mrb_int is 64 bits the smallest Integer is -(2**63), and it cannot stay a
# big integer, since the same value built out of fixnums alone has to be
# indistinguishable from it. The exponent that is not this build's is asked
# too, where both spellings already share one representation, two fixnums on
# a 64 bit build and two big integers on a 32 bit one, and the rows hold for
# that reason.
[31, 63].each do |e|
from_bigint = -(2 ** e)
from_fixnum = -(2 ** e - 1) - 1

assert_equal from_fixnum, from_bigint
assert_true from_bigint.eql?(from_fixnum)
assert_true from_fixnum.eql?(from_bigint)
assert_equal from_fixnum.hash, from_bigint.hash
Comment thread
coderabbitai[bot] marked this conversation as resolved.
end
end

assert 'Bigint +' do
n = 1<<65
assert_equal 36893488147419103232, n + 0
Expand Down
Loading