|
6 | 6 |
|
7 | 7 | from random import random |
8 | 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 |
9 | 12 | import operator |
10 | 13 |
|
11 | 14 | INF = float("inf") |
@@ -330,6 +333,31 @@ def test_pow_with_small_integer_exponents(self): |
330 | 333 | self.assertEqual(str(float_pow), str(int_pow)) |
331 | 334 | self.assertEqual(str(complex_pow), str(int_pow)) |
332 | 335 |
|
| 336 | + # Check that complex numbers with special components |
| 337 | + # are correctly handled. |
| 338 | + values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2, |
| 339 | + -3, INF, -INF, NAN], 2)] |
| 340 | + exponents = [0, 1, 2, 3, 4, 5, 6, 19] |
| 341 | + for z in values: |
| 342 | + for e in exponents: |
| 343 | + try: |
| 344 | + r_pow = z**e |
| 345 | + except OverflowError: |
| 346 | + continue |
| 347 | + r_pro = reduce(lambda x, y: x*y, [z]*e) if e else 1+0j |
| 348 | + test = str(r_pow) == str(r_pro) |
| 349 | + if not test: |
| 350 | + # We might fail here, because associativity of multiplication |
| 351 | + # is broken already for floats. |
| 352 | + # Consider z = 1-1j. Then z*z*z*z = ((z*z)*z)*z = -4+0j, |
| 353 | + # while in the algorithm for pow() a diffenent grouping |
| 354 | + # of operations is used: z**4 = (z*z)*(z*z) = -4-0j. |
| 355 | + r_pro = exp(e*log(z)) |
| 356 | + self.assertTrue(test or isclose(r_pow, r_pro)) |
| 357 | + if not cisnan(r_pow): |
| 358 | + self.assertEqual(copysign(1, r_pow.real), copysign(1, r_pro.real)) |
| 359 | + self.assertEqual(copysign(1, r_pow.imag), copysign(1, r_pro.imag)) |
| 360 | + |
333 | 361 | def test_boolcontext(self): |
334 | 362 | for i in range(100): |
335 | 363 | self.assertTrue(complex(random() + 1e-6, random() + 1e-6)) |
|
0 commit comments