summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarcandre <marcandre@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-02-05 05:39:33 +0000
committermarcandre <marcandre@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-02-05 05:39:33 +0000
commit8797ebd662954c96a25bac1648279431a3a378a8 (patch)
tree6569f60a29873f16cf46033422e34aee03a5d644
parent075ae2955cc34980005a3bd3327049d28d1c1a25 (diff)
* rational.c (nurat_expt): Deal with special cases for rationals 0, ±1
[bug #5713] [bug #5715] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@39063 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--rational.c46
-rw-r--r--test/ruby/test_rational.rb20
2 files changed, 66 insertions, 0 deletions
diff --git a/rational.c b/rational.c
index 626d29565e..0cba54e281 100644
--- a/rational.c
+++ b/rational.c
@@ -221,6 +221,26 @@ f_one_p(VALUE x)
}
inline static VALUE
+f_minus_one_p(VALUE x)
+{
+ switch (TYPE(x)) {
+ case T_FIXNUM:
+ return f_boolcast(FIX2LONG(x) == -1);
+ case T_BIGNUM:
+ return Qfalse;
+ case T_RATIONAL:
+ {
+ VALUE num = RRATIONAL(x)->num;
+ VALUE den = RRATIONAL(x)->den;
+
+ return f_boolcast(FIXNUM_P(num) && FIX2LONG(num) == -1 &&
+ FIXNUM_P(den) && FIX2LONG(den) == 1);
+ }
+ }
+ return rb_funcall(x, id_eqeq_p, 1, INT2FIX(-1));
+}
+
+inline static VALUE
f_kind_of_p(VALUE x, VALUE c)
{
return rb_obj_is_kind_of(x, c);
@@ -916,6 +936,16 @@ nurat_fdiv(VALUE self, VALUE other)
return f_to_f(f_div(self, other));
}
+inline static VALUE
+f_odd_p(VALUE integer)
+{
+ if (rb_funcall(integer, '%', 1, INT2FIX(2)) != INT2FIX(0)) {
+ return Qtrue;
+ }
+ return Qfalse;
+
+}
+
/*
* call-seq:
* rat ** numeric -> numeric
@@ -942,6 +972,22 @@ nurat_expt(VALUE self, VALUE other)
other = dat->num; /* c14n */
}
+ /* Deal with special cases of 0**n and 1**n */
+ if (k_numeric_p(other) && k_exact_p(other)) {
+ get_dat1(self);
+ if (f_one_p(dat->den))
+ if (f_one_p(dat->num))
+ return f_rational_new_bang1(CLASS_OF(self), ONE);
+ else if (f_minus_one_p(dat->num) && k_integer_p(other))
+ return f_rational_new_bang1(CLASS_OF(self), INT2FIX(f_odd_p(other) ? -1 : 1));
+ else if (f_zero_p(dat->num))
+ if (FIX2INT(f_cmp(other, ZERO)) == -1)
+ rb_raise_zerodiv();
+ else
+ return f_rational_new_bang1(CLASS_OF(self), ZERO);
+ }
+
+ /* General case */
switch (TYPE(other)) {
case T_FIXNUM:
{
diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb
index 208099a4ab..0496c0afe3 100644
--- a/test/ruby/test_rational.rb
+++ b/test/ruby/test_rational.rb
@@ -1143,6 +1143,26 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(1.0, Rational(n + 2, n + 1).to_f, '[ruby-dev:33852]')
end
+ def test_power_of_1_and_minus_1
+ bug5715 = '[ruby-core:41498]'
+ big = 1 << 66
+ one = Rational( 1, 1)
+ assert_eql one, one ** -big , bug5715
+ assert_eql one, (-one) ** -big , bug5715
+ assert_eql -one, (-one) ** -(big+1) , bug5715
+ assert_equal Complex, ((-one) ** Rational(1,3)).class
+ end
+
+ def test_power_of_0
+ bug5713 = '[ruby-core:41494]'
+ big = 1 << 66
+ zero = Rational(0, 1)
+ assert_eql zero, zero ** big
+ assert_eql zero, zero ** Rational(2, 3)
+ assert_raise(ZeroDivisionError, bug5713) { Rational(0, 1) ** -big }
+ assert_raise(ZeroDivisionError, bug5713) { Rational(0, 1) ** Rational(-2,3) }
+ end
+
def test_known_bug
end