summaryrefslogtreecommitdiff
path: root/numeric.c
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-05-09 03:16:16 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-05-09 03:16:16 +0000
commit587035c080a8d3bf7016ee70f4841c1af40d36c0 (patch)
tree21c90a12c8e972e2374c6d66fe20cfa87016d23f /numeric.c
parent8dcf078f72152b2d3a409ff8b8b044328b4dce9c (diff)
* numeric.c (int_pow): calculate power in Fixnum as possible.
[ruby-dev:30726] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@12262 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'numeric.c')
-rw-r--r--numeric.c36
1 files changed, 35 insertions, 1 deletions
diff --git a/numeric.c b/numeric.c
index 23d35a1cd5..66d8bb330e 100644
--- a/numeric.c
+++ b/numeric.c
@@ -2177,6 +2177,40 @@ fix_divmod(x, y)
return rb_num_coerce_bin(x, y);
}
+static VALUE
+int_pow(x, y)
+ long x;
+ unsigned long y;
+{
+ int neg = x < 0;
+ long z = 1;
+
+ if (neg) x = -x;
+ if (y & 1) z = x;
+ y &= ~1;
+ do {
+ while (y % 2 == 0) {
+ long x2 = x * x;
+ if (x2 < x || !POSFIXABLE(x2)) {
+ bignum:
+ return rb_big_mul(rb_big_pow(rb_int2big(x), LONG2NUM(y)),
+ rb_int2big(neg ? -z : z));
+ }
+ x = x2;
+ y >>= 1;
+ }
+ {
+ long xz = x * z;
+ if (xz < z || xz < x || !POSFIXABLE(xz)) {
+ goto bignum;
+ }
+ z = xz;
+ }
+ } while (--y);
+ if (neg) z = -z;
+ return LONG2NUM(z);
+}
+
/*
* call-seq:
* fix ** other => Numeric
@@ -2201,7 +2235,7 @@ fix_pow(x, y)
if (b == 1) return x;
a = FIX2LONG(x);
if (b > 0) {
- return rb_big_pow(rb_int2big(a), y);
+ return int_pow(a, b);
}
return rb_float_new(pow((double)a, (double)b));
} else if (TYPE(y) == T_FLOAT) {