[Bug #11183] Fix rb_complex_pow for special angles
Add a special treatment for when the argument of self is an integral multiple of 45 degrees. 1i ** (10 ** 100) #=> 1+0i 1i ** (10 ** 100 + 1) #=> 0+1i (1+1i) ** (10 ** 100) # warning: in a**b, b may be too big #=> (Infinity+0.0i) (1+1i) ** (10 ** 100 + 1) # warning: in a**b, b may be too big #=> (Infinity+Infinity*i)
This commit is contained in:
parent
b6b31f673d
commit
04eb40b633
86
complex.c
86
complex.c
@ -982,6 +982,84 @@ f_reciprocal(VALUE x)
|
||||
return f_quo(ONE, x);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
zero_for(VALUE x)
|
||||
{
|
||||
if (RB_FLOAT_TYPE_P(x))
|
||||
return DBL2NUM(0);
|
||||
if (RB_TYPE_P(x, T_RATIONAL))
|
||||
return rb_rational_new(INT2FIX(0), INT2FIX(1));
|
||||
|
||||
return INT2FIX(0);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
complex_pow_for_special_angle(VALUE self, VALUE other)
|
||||
{
|
||||
if (!rb_integer_type_p(other)) {
|
||||
return Qundef;
|
||||
}
|
||||
|
||||
get_dat1(self);
|
||||
VALUE x = Qundef;
|
||||
int dir;
|
||||
if (f_zero_p(dat->imag)) {
|
||||
x = dat->real;
|
||||
dir = 0;
|
||||
}
|
||||
else if (f_zero_p(dat->real)) {
|
||||
x = dat->imag;
|
||||
dir = 2;
|
||||
}
|
||||
else if (f_eqeq_p(dat->real, dat->imag)) {
|
||||
x = dat->real;
|
||||
dir = 1;
|
||||
}
|
||||
else if (f_eqeq_p(dat->real, f_negate(dat->imag))) {
|
||||
x = dat->imag;
|
||||
dir = 3;
|
||||
}
|
||||
|
||||
if (x == Qundef) return x;
|
||||
|
||||
if (f_negative_p(x)) {
|
||||
x = f_negate(x);
|
||||
dir += 4;
|
||||
}
|
||||
|
||||
VALUE zx;
|
||||
if (dir % 2 == 0) {
|
||||
zx = rb_num_pow(x, other);
|
||||
}
|
||||
else {
|
||||
zx = rb_num_pow(
|
||||
rb_funcall(rb_int_mul(TWO, x), '*', 1, x),
|
||||
rb_int_div(other, TWO)
|
||||
);
|
||||
if (rb_int_odd_p(other)) {
|
||||
zx = rb_funcall(zx, '*', 1, x);
|
||||
}
|
||||
}
|
||||
static const int dirs[][2] = {
|
||||
{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}
|
||||
};
|
||||
int z_dir = FIX2INT(rb_int_modulo(rb_int_mul(INT2FIX(dir), other), INT2FIX(8)));
|
||||
|
||||
VALUE zr, zi;
|
||||
switch (dirs[z_dir][0]) {
|
||||
case 0: zr = zero_for(zx); break;
|
||||
case 1: zr = zx; break;
|
||||
case -1: zr = f_negate(zx); break;
|
||||
}
|
||||
switch (dirs[z_dir][1]) {
|
||||
case 0: zi = zero_for(zx); break;
|
||||
case 1: zi = zx; break;
|
||||
case -1: zi = f_negate(zx); break;
|
||||
}
|
||||
return nucomp_s_new_internal(CLASS_OF(self), zr, zi);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* cmp ** numeric -> complex
|
||||
@ -1007,6 +1085,14 @@ rb_complex_pow(VALUE self, VALUE other)
|
||||
other = dat->real; /* c14n */
|
||||
}
|
||||
|
||||
if (other == ONE) {
|
||||
get_dat1(self);
|
||||
return nucomp_s_new_internal(CLASS_OF(self), dat->real, dat->imag);
|
||||
}
|
||||
|
||||
VALUE result = complex_pow_for_special_angle(self, other);
|
||||
if (result != Qundef) return result;
|
||||
|
||||
if (RB_TYPE_P(other, T_COMPLEX)) {
|
||||
VALUE r, theta, nr, ntheta;
|
||||
|
||||
|
@ -526,6 +526,71 @@ class Complex_Test < Test::Unit::TestCase
|
||||
r = c ** Rational(-2,3)
|
||||
assert_in_delta(0.432, r.real, 0.001)
|
||||
assert_in_delta(-0.393, r.imag, 0.001)
|
||||
end
|
||||
|
||||
def test_expt_for_special_angle
|
||||
c = Complex(1, 0) ** 100000000000000000000000000000000
|
||||
assert_equal(Complex(1, 0), c)
|
||||
|
||||
c = Complex(-1, 0) ** 10000000000000000000000000000000
|
||||
assert_equal(Complex(1, 0), c)
|
||||
|
||||
c = Complex(-1, 0) ** 10000000000000000000000000000001
|
||||
assert_equal(Complex(-1, 0), c)
|
||||
|
||||
c = Complex(0, 1) ** 100000000000000000000000000000000
|
||||
assert_equal(Complex(1, 0), c)
|
||||
|
||||
c = Complex(0, 1) ** 100000000000000000000000000000001
|
||||
assert_equal(Complex(0, 1), c)
|
||||
|
||||
c = Complex(0, 1) ** 100000000000000000000000000000002
|
||||
assert_equal(Complex(-1, 0), c)
|
||||
|
||||
c = Complex(0, 1) ** 100000000000000000000000000000003
|
||||
assert_equal(Complex(0, -1), c)
|
||||
|
||||
c = Complex(0, -1) ** 100000000000000000000000000000000
|
||||
assert_equal(Complex(1, 0), c)
|
||||
|
||||
c = Complex(0, -1) ** 100000000000000000000000000000001
|
||||
assert_equal(Complex(0, -1), c)
|
||||
|
||||
c = Complex(0, -1) ** 100000000000000000000000000000002
|
||||
assert_equal(Complex(-1, 0), c)
|
||||
|
||||
c = Complex(0, -1) ** 100000000000000000000000000000003
|
||||
assert_equal(Complex(0, 1), c)
|
||||
|
||||
c = Complex(1, 1) ** 1
|
||||
assert_equal(Complex(1, 1), c)
|
||||
|
||||
c = Complex(1, 1) ** 2
|
||||
assert_equal(Complex(0, 2), c)
|
||||
|
||||
c = Complex(1, 1) ** 3
|
||||
assert_equal(Complex(-2, 2), c)
|
||||
|
||||
c = Complex(1, 1) ** 4
|
||||
assert_equal(Complex(-4, 0), c)
|
||||
|
||||
c = Complex(1, 1) ** 5
|
||||
assert_equal(Complex(-4, -4), c)
|
||||
|
||||
c = Complex(1, 1) ** 6
|
||||
assert_equal(Complex(0, -8), c)
|
||||
|
||||
c = Complex(1, 1) ** 7
|
||||
assert_equal(Complex(8, -8), c)
|
||||
|
||||
c = Complex(-2, -2) ** 3
|
||||
assert_equal(Complex(16, -16), c)
|
||||
|
||||
c = Complex(2, -2) ** 3
|
||||
assert_equal(Complex(-16, -16), c)
|
||||
|
||||
c = Complex(-2, 2) ** 3
|
||||
assert_equal(Complex(16, 16), c)
|
||||
|
||||
c = Complex(0.0, -888888888888888.0)**8888
|
||||
assert_not_predicate(c.real, :nan?)
|
||||
|
Loading…
x
Reference in New Issue
Block a user