|
9 | 9 |
|
10 | 10 | from random import random |
11 | 11 | from math import isnan, copysign |
12 | | -from functools import reduce |
13 | | -from itertools import combinations_with_replacement |
14 | 12 | import operator |
15 | | -import _testcapi |
16 | 13 |
|
17 | 14 | INF = float("inf") |
18 | 15 | NAN = float("nan") |
@@ -92,6 +89,10 @@ def assertClose(self, x, y, eps=1e-9): |
92 | 89 | self.assertCloseAbs(x.real, y.real, eps) |
93 | 90 | self.assertCloseAbs(x.imag, y.imag, eps) |
94 | 91 |
|
| 92 | + def assertSameSign(self, x, y): |
| 93 | + if copysign(1., x) != copysign(1., y): |
| 94 | + self.fail(f'{x!r} and {y!r} have different signs') |
| 95 | + |
95 | 96 | def check_div(self, x, y): |
96 | 97 | """Compute complex z=x*y, and check that z/x==y and z/y==x.""" |
97 | 98 | z = x * y |
@@ -452,44 +453,60 @@ def test_pow_with_small_integer_exponents(self): |
452 | 453 |
|
453 | 454 | # Check that complex numbers with special components |
454 | 455 | # are correctly handled. |
455 | | - values = [complex(*_) |
456 | | - for _ in combinations_with_replacement([1, -1, 0.0, 0, -0.0, 2, |
457 | | - -3, INF, -INF, NAN], 2)] |
458 | | - exponents = [0, 1, 2, 3, 4, 5, 6, 19] |
459 | | - for z in values: |
460 | | - for e in exponents: |
461 | | - with self.subTest(value=z, exponent=e): |
462 | | - try: |
463 | | - r_pow = z**e |
464 | | - except OverflowError: |
465 | | - continue |
466 | | - r_pro = reduce(lambda x, y: x*y, [z]*e) if e else 1+0j |
467 | | - if str(r_pow) == str(r_pro): |
468 | | - continue |
469 | | - |
470 | | - self.assertNotIn(z.real, {0.0, -0.0, INF, -INF, NAN}) |
471 | | - self.assertNotIn(z.imag, {0.0, -0.0, INF, -INF, NAN}) |
472 | | - |
473 | | - # We might fail here, because associativity of multiplication |
474 | | - # is broken already for floats. |
475 | | - # Consider z = 1-1j. Then z*z*z*z = ((z*z)*z)*z = -4+0j, |
476 | | - # while in the algorithm for pow() a diffenent grouping |
477 | | - # of operations is used: z**4 = (z*z)*(z*z) = -4-0j. |
478 | | - # |
479 | | - # Fallback to the generic complex power algorithm. |
480 | | - r_pro, r_pro_errno = _testcapi._py_c_pow(z, e) |
481 | | - self.assertEqual(r_pro_errno, 0) |
482 | | - self.assertClose(r_pow, r_pro) |
483 | | - if isnan(r_pow.real): |
484 | | - self.assertTrue(isnan(r_pro.real)) |
485 | | - else: |
486 | | - self.assertEqual(copysign(1, r_pow.real), |
487 | | - copysign(1, r_pro.real)) |
488 | | - if isnan(r_pow.imag): |
489 | | - self.assertTrue(isnan(r_pro.imag)) |
490 | | - else: |
491 | | - self.assertEqual(copysign(1, r_pow.imag), |
492 | | - copysign(1, r_pro.imag)) |
| 456 | + values = [complex(x, y) |
| 457 | + for x in [5, -5, +0.0, -0.0, INF, -INF, NAN] |
| 458 | + for y in [12, -12, +0.0, -0.0, INF, -INF, NAN]] |
| 459 | + for c in values: |
| 460 | + with self.subTest(value=c): |
| 461 | + self.assertComplexesAreIdentical(c**0, complex(1, +0.0)) |
| 462 | + self.assertComplexesAreIdentical(c**1, c) |
| 463 | + self.assertComplexesAreIdentical(c**2, c*c) |
| 464 | + self.assertComplexesAreIdentical(c**3, c*(c*c)) |
| 465 | + self.assertComplexesAreIdentical(c**3, (c*c)*c) |
| 466 | + if not c: |
| 467 | + continue |
| 468 | + for n in range(1, 9): |
| 469 | + with self.subTest(exponent=-n): |
| 470 | + self.assertComplexesAreIdentical(c**-n, 1/(c**n)) |
| 471 | + |
| 472 | + # Special cases for complex division. |
| 473 | + for x in [+2, -2]: |
| 474 | + for y in [+0.0, -0.0]: |
| 475 | + c = complex(x, y) |
| 476 | + with self.subTest(value=c): |
| 477 | + self.assertComplexesAreIdentical(c**-1, complex(1/x, -y)) |
| 478 | + c = complex(y, x) |
| 479 | + with self.subTest(value=c): |
| 480 | + self.assertComplexesAreIdentical(c**-1, complex(y, -1/x)) |
| 481 | + for x in [+INF, -INF]: |
| 482 | + for y in [+1, -1]: |
| 483 | + c = complex(x, y) |
| 484 | + with self.subTest(value=c): |
| 485 | + self.assertComplexesAreIdentical(c**-1, complex(1/x, -0.0*y)) |
| 486 | + self.assertComplexesAreIdentical(c**-2, complex(0.0, -y/x)) |
| 487 | + c = complex(y, x) |
| 488 | + with self.subTest(value=c): |
| 489 | + self.assertComplexesAreIdentical(c**-1, complex(+0.0*y, -1/x)) |
| 490 | + self.assertComplexesAreIdentical(c**-2, complex(-0.0, -y/x)) |
| 491 | + |
| 492 | + # Test that zeroes has the same sign as small non-zero values. |
| 493 | + eps = 1e-8 |
| 494 | + pairs = [(complex(x, y), complex(x, copysign(0.0, y))) |
| 495 | + for x in [+1, -1] for y in [+eps, -eps]] |
| 496 | + pairs += [(complex(y, x), complex(copysign(0.0, y), x)) |
| 497 | + for x in [+1, -1] for y in [+eps, -eps]] |
| 498 | + for c1, c2 in pairs: |
| 499 | + for n in exponents: |
| 500 | + with self.subTest(value=c1, exponent=n): |
| 501 | + r1 = c1**n |
| 502 | + r2 = c2**n |
| 503 | + self.assertClose(r1, r2) |
| 504 | + self.assertSameSign(r1.real, r2.real) |
| 505 | + self.assertSameSign(r1.imag, r2.imag) |
| 506 | + self.assertNotEqual(r1.real, 0.0) |
| 507 | + if n != 0: |
| 508 | + self.assertNotEqual(r1.imag, 0.0) |
| 509 | + self.assertTrue(r2.real == 0.0 or r2.imag == 0.0) |
493 | 510 |
|
494 | 511 | def test_boolcontext(self): |
495 | 512 | for i in range(100): |
|
0 commit comments