summaryrefslogtreecommitdiff
path: root/bignum.c
diff options
context:
space:
mode:
authormatz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-04-20 17:53:36 +0000
committermatz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-04-20 17:53:36 +0000
commitdd54da6edeaa576ece85ec652612a5c93e7a4883 (patch)
treeaa6468e7948ba925d984bcaf4a45f6273df827ea /bignum.c
parent5fc9b700bbb6c43fae78c20a278404c882f9139c (diff)
* 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.c142
1 files changed, 136 insertions, 6 deletions
diff --git a/bignum.c b/bignum.c
index 28b64d9f20..a6589a2531 100644
--- a/bignum.c
+++ b/bignum.c
@@ -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));