diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 3d02bb6ec2389b..5011a753744fca 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -517,6 +517,14 @@ def test_pow_with_small_integer_exponents(self): self.assertNotEqual(r1.imag, 0.0) self.assertTrue(r2.real == 0.0 or r2.imag == 0.0) + def test_pow_small_negative_integer_exponents(self): + for z, expected in [(complex(2**86, 0), complex(2**-1032, 0)), + (complex(0, 2**86), complex(2**-1032, 0)), + (complex(2**85, 2**85), complex(-2**-1026, 0))]: + for exponent in (-12, -12.0, complex(-12)): + with self.subTest(z=z, exponent=exponent): + self.assertEqual(z**exponent, expected) + def test_boolcontext(self): for i in range(100): self.assertTrue(complex(random() + 1e-6, random() + 1e-6)) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-01-09-09-13.gh-issue-156695.-Gih-8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-01-09-09-13.gh-issue-156695.-Gih-8.rst new file mode 100644 index 00000000000000..7d96b365ce114e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-01-09-09-13.gh-issue-156695.-Gih-8.rst @@ -0,0 +1,4 @@ +Improve accuracy of :class:`complex` powers with small negative integer +exponents. Previously ``z**-n`` was computed as ``1/(z**n)``; the +intermediate ``z**n`` can overflow even when the result is representable, +in which case all precision was lost. diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 9328baf013c972..069a32602d3cb9 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -370,10 +370,25 @@ c_powi(Py_complex x, long n) { if (n > 0) return c_powu(x, n); - else if (n < 0) - return _Py_rc_quot(1.0, c_powu(x, -n)); - else + else if (n == 0) return (Py_complex){1., 0.}; + + double m = fabs(x.real) > fabs(x.imag) ? x.real : x.imag; + + if (m && isfinite(m)) { + int e; + + frexp(m, &e); + + if (-e*n > 800) { + x = (Py_complex){ldexp(x.real, -e), ldexp(x.imag, -e)}; + x = _Py_rc_quot(1.0, c_powu(x, -n)); + x.real = ldexp(x.real, (int)(e * n)); + x.imag = ldexp(x.imag, (int)(e * n)); + return x; + } + } + return _Py_rc_quot(1.0, c_powu(x, -n)); } double