diff --git a/ChangeLog b/ChangeLog index 5465281c35..9586a8c95e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Thu Jul 19 14:38:45 2007 Nobuyoshi Nakada + + * bignum.c (rb_big_lshift, rb_big_rshift): separated functions + to get rid of infinite recursion. fixed calculation in edge + cases. [ruby-dev:31244] + + * numeric.c (rb_fix_lshift, rb_fix_rshift): ditto. + Wed Jul 18 16:57:41 2007 Nobuyoshi Nakada * bignum.c (rb_big_pow): refine overflow check. [ruby-dev:31242] diff --git a/bignum.c b/bignum.c index fc4b79883a..a0402d7d2d 100644 --- a/bignum.c +++ b/bignum.c @@ -1529,7 +1529,17 @@ bdigbitsize(BDIGIT x) return size; } -static VALUE rb_big_rshift(VALUE,VALUE); +static VALUE big_lshift(VALUE, unsigned int); +static VALUE big_rshift(VALUE, unsigned int); + +static VALUE big_shift(VALUE x, int n) +{ + if (n < 0) + return big_lshift(x, (unsigned int)n); + else if (n > 0) + return big_rshift(x, (unsigned int)n); + return x; +} /* * call-seq: @@ -1558,7 +1568,7 @@ rb_big_quo(VALUE x, VALUE y) ex = (RBIGNUM(bigtrunc(x))->len - 1) * BITSPERDIG; ex += bdigbitsize(BDIGITS(x)[RBIGNUM(x)->len - 1]); ex -= 2 * DBL_BIGDIG * BITSPERDIG; - if (ex) x = rb_big_rshift(x, INT2FIX(ex)); + if (ex) x = big_shift(x, ex); switch (TYPE(y)) { case T_FIXNUM: @@ -1567,7 +1577,7 @@ rb_big_quo(VALUE x, VALUE y) ey = (RBIGNUM(bigtrunc(y))->len - 1) * BITSPERDIG; ey += bdigbitsize(BDIGITS(y)[RBIGNUM(y)->len - 1]); ey -= DBL_BIGDIG * BITSPERDIG; - if (ey) y = rb_big_rshift(y, INT2FIX(ey)); + if (ey) y = big_shift(y, ey); bignum: bigdivrem(x, y, &z, 0); return rb_float_new(ldexp(big2dbl(z), ex - ey)); @@ -1872,6 +1882,16 @@ rb_big_xor(VALUE xx, VALUE yy) return bignorm(z); } +static VALUE +check_shiftdown(VALUE y, VALUE x) +{ + if (!RBIGNUM(x)->len) return INT2FIX(0); + if (RBIGNUM(y)->len > SIZEOF_LONG / SIZEOF_BDIGITS) { + return RBIGNUM(x)->sign ? INT2FIX(0) : INT2FIX(-1); + } + return Qnil; +} + /* * call-seq: * big << numeric => integer @@ -1881,16 +1901,44 @@ rb_big_xor(VALUE xx, VALUE yy) VALUE rb_big_lshift(VALUE x, VALUE y) +{ + int shift, neg = 0; + + for (;;) { + if (FIXNUM_P(y)) { + shift = FIX2INT(y); + if (shift < 0) { + neg = 1; + shift = -shift; + } + break; + } + else if (TYPE(y) == T_BIGNUM) { + if (!RBIGNUM(y)->sign) { + VALUE t = check_shiftdown(y, x); + if (!NIL_P(t)) return t; + neg = 1; + } + shift = big2ulong(y, "long", Qtrue); + break; + } + y = rb_to_int(y); + } + + if (neg) return big_rshift(x, shift); + return big_lshift(x, shift); +} + +static VALUE +big_lshift(VALUE x, unsigned int shift) { BDIGIT *xds, *zds; - int shift = NUM2INT(y); int s1 = shift/BITSPERDIG; int s2 = shift%BITSPERDIG; VALUE z; BDIGIT_DBL num = 0; long len, i; - if (shift < 0) return rb_big_rshift(x, INT2FIX(-shift)); len = RBIGNUM(x)->len; z = bignew(len+s1+1, RBIGNUM(x)->sign); zds = BDIGITS(z); @@ -1914,11 +1962,43 @@ rb_big_lshift(VALUE x, VALUE y) * Shifts big right _numeric_ positions (left if _numeric_ is negative). */ -static VALUE +VALUE rb_big_rshift(VALUE x, VALUE y) +{ + int shift; + int neg = 0; + + for (;;) { + if (FIXNUM_P(y)) { + shift = FIX2INT(y); + if (shift < 0) { + neg = 1; + shift = -shift; + } + break; + } + else if (TYPE(y) == T_BIGNUM) { + if (RBIGNUM(y)->sign) { + VALUE t = check_shiftdown(y, x); + if (!NIL_P(t)) return t; + } + else { + neg = 1; + } + shift = big2ulong(y, "long", Qtrue); + break; + } + y = rb_to_int(y); + } + + if (neg) return big_lshift(x, shift); + return big_rshift(x, shift); +} + +static VALUE +big_rshift(VALUE x, unsigned int shift) { BDIGIT *xds, *zds; - int shift = NUM2INT(y); long s1 = shift/BITSPERDIG; long s2 = shift%BITSPERDIG; VALUE z; @@ -1926,8 +2006,6 @@ rb_big_rshift(VALUE x, VALUE y) long i, j; volatile VALUE save_x; - if (shift < 0) return rb_big_lshift(x, INT2FIX(-shift)); - if (s1 > RBIGNUM(x)->len) { if (RBIGNUM(x)->sign) return INT2FIX(0); diff --git a/include/ruby/intern.h b/include/ruby/intern.h index a9ab902d0e..301337094a 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -114,6 +114,7 @@ VALUE rb_big_and(VALUE, VALUE); VALUE rb_big_or(VALUE, VALUE); VALUE rb_big_xor(VALUE, VALUE); VALUE rb_big_lshift(VALUE, VALUE); +VALUE rb_big_rshift(VALUE, VALUE); /* class.c */ VALUE rb_class_boot(VALUE); VALUE rb_class_new(VALUE); diff --git a/numeric.c b/numeric.c index 9f986a7633..9b723d8581 100644 --- a/numeric.c +++ b/numeric.c @@ -2591,7 +2591,8 @@ fix_xor(VALUE x, VALUE y) return LONG2NUM(val); } -static VALUE fix_rshift(VALUE, VALUE); +static VALUE fix_lshift(long, unsigned long); +static VALUE fix_rshift(long, unsigned long); /* * call-seq: @@ -2601,17 +2602,25 @@ static VALUE fix_rshift(VALUE, VALUE); */ static VALUE -fix_lshift(VALUE x, VALUE y) +rb_fix_lshift(VALUE x, VALUE y) { long val, width; val = NUM2LONG(x); - width = NUM2LONG(y); + if (!FIXNUM_P(y)) + return rb_big_lshift(rb_int2big(val), y); + width = FIX2LONG(y); if (width < 0) - return fix_rshift(x, LONG2FIX(-width)); + return fix_rshift(val, (unsigned long)-width); + return fix_lshift(val, width); +} + +static VALUE +fix_lshift(long val, unsigned long width) +{ if (width > (sizeof(VALUE)*CHAR_BIT-1) || ((unsigned long)val)>>(sizeof(VALUE)*CHAR_BIT-1-width) > 0) { - return rb_big_lshift(rb_int2big(val), y); + return rb_big_lshift(rb_int2big(val), ULONG2NUM(width)); } val = val << width; return LONG2NUM(val); @@ -2625,15 +2634,23 @@ fix_lshift(VALUE x, VALUE y) */ static VALUE -fix_rshift(VALUE x, VALUE y) +rb_fix_rshift(VALUE x, VALUE y) { long i, val; - i = NUM2LONG(y); - if (i < 0) - return fix_lshift(x, LONG2FIX(-i)); - if (i == 0) return x; val = FIX2LONG(x); + if (!FIXNUM_P(y)) + return rb_big_rshift(rb_int2big(val), y); + i = FIX2LONG(y); + if (i == 0) return x; + if (i < 0) + return fix_lshift(val, (unsigned long)-i); + return fix_rshift(val, i); +} + +static VALUE +fix_rshift(long val, unsigned long i) +{ if (i >= sizeof(long)*CHAR_BIT-1) { if (val < 0) return INT2FIX(-1); return INT2FIX(0); @@ -2885,8 +2902,6 @@ int_downto(VALUE from, VALUE to) static VALUE int_dotimes(VALUE num) { - VALUE val; - RETURN_ENUMERATOR(num, 0, 0); if (FIXNUM_P(num)) { @@ -3105,8 +3120,8 @@ Init_Numeric(void) rb_define_method(rb_cFixnum, "^", fix_xor, 1); rb_define_method(rb_cFixnum, "[]", fix_aref, 1); - rb_define_method(rb_cFixnum, "<<", fix_lshift, 1); - rb_define_method(rb_cFixnum, ">>", fix_rshift, 1); + rb_define_method(rb_cFixnum, "<<", rb_fix_lshift, 1); + rb_define_method(rb_cFixnum, ">>", rb_fix_rshift, 1); rb_define_method(rb_cFixnum, "to_f", fix_to_f, 0); rb_define_method(rb_cFixnum, "size", fix_size, 0); diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb index d4b45d8287..dd2be1e2ca 100644 --- a/test/ruby/test_integer.rb +++ b/test/ruby/test_integer.rb @@ -93,6 +93,14 @@ class TestInteger < Test::Unit::TestCase #VS.map! {|v| 0x4000000000000000.coerce(v)[0] } + BDSIZE = 0x4000000000000000.coerce(0)[0].size + def self.bdsize(x) + ((x + 1) / 8 + BDSIZE) / BDSIZE * BDSIZE + end + def bdsize(x) + self.class.bdsize(x) + end + def test_aref VS.each {|a| 100.times {|i| @@ -233,6 +241,11 @@ class TestInteger < Test::Unit::TestCase end } } + assert_equal(0, 1 << -0x40000000) + assert_equal(0, 1 << -0x40000001) + assert_equal(0, 1 << -0x80000000) + assert_equal(0, 1 << -0x80000001) + # assert_equal(bdsize(0x80000000), (1 << 0x80000000).size) end def test_rshift @@ -248,6 +261,12 @@ class TestInteger < Test::Unit::TestCase end } } + # assert_equal(bdsize(0x40000001), (1 >> -0x40000001).size) + assert((1 >> 0x80000000).zero?) + assert((1 >> 0xffffffff).zero?) + assert((1 >> 0x100000000).zero?) + # assert_equal((1 << 0x40000000), (1 >> -0x40000000)) + # assert_equal((1 << 0x40000001), (1 >> -0x40000001)) end def test_succ diff --git a/version.h b/version.h index ec3495b06e..7ae28af2dc 100644 --- a/version.h +++ b/version.h @@ -1,7 +1,7 @@ #define RUBY_VERSION "1.9.0" -#define RUBY_RELEASE_DATE "2007-07-18" +#define RUBY_RELEASE_DATE "2007-07-19" #define RUBY_VERSION_CODE 190 -#define RUBY_RELEASE_CODE 20070718 +#define RUBY_RELEASE_CODE 20070719 #define RUBY_PATCHLEVEL 0 #define RUBY_VERSION_MAJOR 1 @@ -9,7 +9,7 @@ #define RUBY_VERSION_TEENY 0 #define RUBY_RELEASE_YEAR 2007 #define RUBY_RELEASE_MONTH 7 -#define RUBY_RELEASE_DAY 18 +#define RUBY_RELEASE_DAY 19 #ifdef RUBY_EXTERN RUBY_EXTERN const char ruby_version[];