|
5 | 5 | INVALID_UNDERSCORE_LITERALS)
|
6 | 6 |
|
7 | 7 | 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 |
9 | 12 | import operator
|
10 | 13 |
|
11 | 14 | INF = float("inf")
|
@@ -442,6 +445,31 @@ def test_pow_with_small_integer_exponents(self):
|
442 | 445 | self.assertEqual(str(float_pow), str(int_pow))
|
443 | 446 | self.assertEqual(str(complex_pow), str(int_pow))
|
444 | 447 |
|
| 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 | + |
445 | 473 | def test_boolcontext(self):
|
446 | 474 | for i in range(100):
|
447 | 475 | self.assertTrue(complex(random() + 1e-6, random() + 1e-6))
|
|
0 commit comments