Skip to content

Commit 93a2a5b

Browse files
committed
from 117999: fixed small nonnegative integer powers of complex numbers
Before, handling of numbers with special values in components (infinities, nans, signed zero) was invalid. Simple example: >>> z = complex(1, -0.0) >>> z*z (1-0j) >>> z**2 (1+0j) Now: >>> z**2 (1-0j)
1 parent 598cb00 commit 93a2a5b

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

Lib/test/test_complex.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
INVALID_UNDERSCORE_LITERALS)
66

77
from random import random
8-
from math import isnan, copysign
8+
from math import atan2, isnan, copysign
9+
from cmath import log, exp, isclose, isnan as cisnan
10+
from functools import reduce
11+
from itertools import combinations
912
import operator
1013

1114
INF = float("inf")
@@ -442,6 +445,31 @@ def test_pow_with_small_integer_exponents(self):
442445
self.assertEqual(str(float_pow), str(int_pow))
443446
self.assertEqual(str(complex_pow), str(int_pow))
444447

448+
# Check that complex numbers with special components
449+
# are correctly handled.
450+
values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
451+
-3, INF, -INF, NAN], 2)]
452+
exponents = [0, 1, 2, 3, 4, 5, 6, 19]
453+
for z in values:
454+
for e in exponents:
455+
try:
456+
r_pow = z**e
457+
except OverflowError:
458+
continue
459+
r_pro = reduce(lambda x, y: x*y, [z]*e) if e else 1+0j
460+
test = str(r_pow) == str(r_pro)
461+
if not test:
462+
# We might fail here, because associativity of multiplication
463+
# is broken already for floats.
464+
# Consider z = 1-1j. Then z*z*z*z = ((z*z)*z)*z = -4+0j,
465+
# while in the algorithm for pow() a diffenent grouping
466+
# of operations is used: z**4 = (z*z)*(z*z) = -4-0j.
467+
r_pro = exp(e*log(z))
468+
self.assertTrue(test or isclose(r_pow, r_pro))
469+
if not cisnan(r_pow):
470+
self.assertEqual(copysign(1, r_pow.real), copysign(1, r_pro.real))
471+
self.assertEqual(copysign(1, r_pow.imag), copysign(1, r_pro.imag))
472+
445473
def test_boolcontext(self):
446474
for i in range(100):
447475
self.assertTrue(complex(random() + 1e-6, random() + 1e-6))

Objects/complexobject.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,8 @@ _Py_c_pow(Py_complex a, Py_complex b)
194194
static Py_complex
195195
c_powu(Py_complex x, long n)
196196
{
197-
Py_complex r, p;
197+
Py_complex p = x, r = n-- ? p : c_1;
198198
long mask = 1;
199-
r = c_1;
200-
p = x;
201199
while (mask > 0 && n >= mask) {
202200
if (n & mask)
203201
r = _Py_c_prod(r,p);
@@ -210,11 +208,10 @@ c_powu(Py_complex x, long n)
210208
static Py_complex
211209
c_powi(Py_complex x, long n)
212210
{
213-
if (n > 0)
214-
return c_powu(x,n);
215-
else
211+
if (n < 0)
216212
return _Py_c_quot(c_1, c_powu(x,-n));
217-
213+
else
214+
return c_powu(x,n);
218215
}
219216

220217
double

0 commit comments

Comments
 (0)