diff options
author | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2009-04-20 17:53:36 +0000 |
---|---|---|
committer | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2009-04-20 17:53:36 +0000 |
commit | dd54da6edeaa576ece85ec652612a5c93e7a4883 (patch) | |
tree | aa6468e7948ba925d984bcaf4a45f6273df827ea /bignum.c | |
parent | 5fc9b700bbb6c43fae78c20a278404c882f9139c (diff) | |
download | ruby-dd54da6edeaa576ece85ec652612a5c93e7a4883.tar.gz |
* bignum.c (bigsub_int): subtraction without making internal
bignum values.
* bignum.c (bigadd_int): ditto for addition.
* bignum.c (bigtrunc): declare inline.
* bignum.c (rb_quad_pack): fix condition.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@23238 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'bignum.c')
-rw-r--r-- | bignum.c | 142 |
1 files changed, 136 insertions, 6 deletions
@@ -178,7 +178,7 @@ rb_big_2comp(VALUE x) /* get 2's complement */ get2comp(x); } -static VALUE +static inline VALUE bigtrunc(VALUE x) { long len = RBIGNUM_LEN(x); @@ -285,7 +285,7 @@ rb_int2inum(SIGNED_VALUE n) return rb_int2big(n); } -#ifdef HAVE_LONG_LONG +#if SIZEOF_BDIGITS*2 == SIZEOF_LONG_LONG void rb_quad_pack(char *buf, VALUE val) @@ -1450,6 +1450,112 @@ bigsub(VALUE x, VALUE y) return z; } +static VALUE bigadd_int(VALUE x, long y); + +static VALUE +bigsub_int(VALUE x, long y0) +{ + VALUE z; + BDIGIT *xds, *zds; + long xn; + BDIGIT_DBL_SIGNED num; + long i, y; + + y = y0; + xds = BDIGITS(x); + xn = RBIGNUM_LEN(x); + + z = bignew(xn, RBIGNUM_SIGN(x)); + zds = BDIGITS(z); + +#if SIZEOF_BDIGITS == SIZEOF_LONG + num = (BDIGIT_DBL_SIGNED)xds[0] - y; + if (xn == 1 && num < 0) { + for (i=0; i<xn; i++) { + } + RBIGNUM_SET_SIGN(z, !RBIGNUM_SIGN(x)); + zds[0] = -num; + return bignorm(z); + } + zds[0] = BIGLO(num); + num = BIGDN(num); + i = 1; +#else + num = 0; + for (i=0; i<sizeof(y)/sizeof(BDIGIT); i++) { + num += (BDIGIT_DBL_SIGNED)xds[i] - BIGLO(y); + zds[i] = BIGLO(num); + num = BIGDN(num); + y = BIGDN(y); + } +#endif + while (num && i < xn) { + num += xds[i]; + zds[i++] = BIGLO(num); + num = BIGDN(num); + } + while (i < xn) { + zds[i] = xds[i]; + i++; + } + if (num < 0) { + z = bigsub(x, rb_int2big(y0)); + } + return bignorm(z); +} + +static VALUE +bigadd_int(VALUE x, long y) +{ + VALUE z; + BDIGIT *xds, *zds; + long xn, zn; + BDIGIT_DBL num; + long i; + + xds = BDIGITS(x); + xn = RBIGNUM_LEN(x); + + if (xn < 2) { + zn = 3; + } + else { + zn = xn + 1; + } + z = bignew(zn, RBIGNUM_SIGN(x)); + zds = BDIGITS(z); + +#if SIZEOF_BDIGITS == SIZEOF_LONG + num = (BDIGIT_DBL)xds[0] + y; + zds[0] = BIGLO(num); + num = BIGDN(num); + i = 1; +#else + num = 0; + for (i=0; i<sizeof(y)/sizeof(BDIGIT); i++) { + num += (BDIGIT_DBL)xds[i] + BIGLO(y); + zds[i] = BIGLO(num); + num = BIGDN(num); + y = BIGDN(y); + } +#endif + while (num && i < xn) { + num += xds[i]; + zds[i++] = BIGLO(num); + num = BIGDN(num); + } + if (num) zds[i++] = (BDIGIT)num; + else while (i < xn) { + zds[i] = xds[i]; + i++; + } + assert(i <= zn); + while (i < zn) { + zds[i++] = 0; + } + return bignorm(z); +} + static void bigadd_core(BDIGIT *xds, long xn, BDIGIT *yds, long yn, BDIGIT *zds, long zn) { @@ -1521,10 +1627,22 @@ bigadd(VALUE x, VALUE y, int sign) VALUE rb_big_plus(VALUE x, VALUE y) { + long n; + switch (TYPE(y)) { case T_FIXNUM: - y = rb_int2big(FIX2LONG(y)); - /* fall through */ + n = FIX2LONG(y); + if ((n > 0) != RBIGNUM_SIGN(x)) { + if (n < 0) { + n = -n; + } + return bigsub_int(x, n); + } + if (n < 0) { + n = -n; + } + return bigadd_int(x, n); + case T_BIGNUM: return bignorm(bigadd(x, y, 1)); @@ -1546,10 +1664,22 @@ rb_big_plus(VALUE x, VALUE y) VALUE rb_big_minus(VALUE x, VALUE y) { + long n; + switch (TYPE(y)) { case T_FIXNUM: - y = rb_int2big(FIX2LONG(y)); - /* fall through */ + n = FIX2LONG(y); + if ((n > 0) != RBIGNUM_SIGN(x)) { + if (n < 0) { + n = -n; + } + return bigadd_int(x, n); + } + if (n < 0) { + n = -n; + } + return bigsub_int(x, n); + case T_BIGNUM: return bignorm(bigadd(x, y, 0)); |