Skip to content

Commit 2cb84b1

Browse files
authored
gh-119372: Recover inf's and zeros in _Py_c_quot (GH-119457)
In some cases, previously computed as (nan+nanj), we could recover meaningful component values in the result, see e.g. the C11, Annex G.5.2, routine _Cdivd().
1 parent 0a1e8ff commit 2cb84b1

File tree

3 files changed

+56
-2
lines changed

3 files changed

+56
-2
lines changed

Lib/test/test_complex.py

+31
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ def assertFloatsAreIdentical(self, x, y):
9494
msg += ': zeros have different signs'
9595
self.fail(msg.format(x, y))
9696

97+
def assertComplexesAreIdentical(self, x, y):
98+
self.assertFloatsAreIdentical(x.real, y.real)
99+
self.assertFloatsAreIdentical(x.imag, y.imag)
100+
97101
def assertClose(self, x, y, eps=1e-9):
98102
"""Return true iff complexes x and y "are close"."""
99103
self.assertCloseAbs(x.real, y.real, eps)
@@ -139,6 +143,33 @@ def test_truediv(self):
139143
self.assertTrue(isnan(z.real))
140144
self.assertTrue(isnan(z.imag))
141145

146+
self.assertComplexesAreIdentical(complex(INF, 1)/(0.0+1j),
147+
complex(NAN, -INF))
148+
149+
# test recover of infs if numerator has infs and denominator is finite
150+
self.assertComplexesAreIdentical(complex(INF, -INF)/(1+0j),
151+
complex(INF, -INF))
152+
self.assertComplexesAreIdentical(complex(INF, INF)/(0.0+1j),
153+
complex(INF, -INF))
154+
self.assertComplexesAreIdentical(complex(NAN, INF)/complex(2**1000, 2**-1000),
155+
complex(INF, INF))
156+
self.assertComplexesAreIdentical(complex(INF, NAN)/complex(2**1000, 2**-1000),
157+
complex(INF, -INF))
158+
159+
# test recover of zeros if denominator is infinite
160+
self.assertComplexesAreIdentical((1+1j)/complex(INF, INF), (0.0+0j))
161+
self.assertComplexesAreIdentical((1+1j)/complex(INF, -INF), (0.0+0j))
162+
self.assertComplexesAreIdentical((1+1j)/complex(-INF, INF),
163+
complex(0.0, -0.0))
164+
self.assertComplexesAreIdentical((1+1j)/complex(-INF, -INF),
165+
complex(-0.0, 0))
166+
self.assertComplexesAreIdentical((INF+1j)/complex(INF, INF),
167+
complex(NAN, NAN))
168+
self.assertComplexesAreIdentical(complex(1, INF)/complex(INF, INF),
169+
complex(NAN, NAN))
170+
self.assertComplexesAreIdentical(complex(INF, 1)/complex(1, INF),
171+
complex(NAN, NAN))
172+
142173
def test_truediv_zero_division(self):
143174
for a, b in ZERO_DIVISION:
144175
with self.assertRaises(ZeroDivisionError):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Correct invalid corner cases in complex division (resulted in ``(nan+nanj)``
2+
output), e.g. ``1/complex('(inf+infj)')``. Patch by Sergey B Kirpichev.

Objects/complexobject.c

+23-2
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ _Py_c_quot(Py_complex a, Py_complex b)
8888
* numerators and denominator by whichever of {b.real, b.imag} has
8989
* larger magnitude. The earliest reference I found was to CACM
9090
* Algorithm 116 (Complex Division, Robert L. Smith, Stanford
91-
* University). As usual, though, we're still ignoring all IEEE
92-
* endcases.
91+
* University).
9392
*/
9493
Py_complex r; /* the result */
9594
const double abs_breal = b.real < 0 ? -b.real : b.real;
@@ -120,6 +119,28 @@ _Py_c_quot(Py_complex a, Py_complex b)
120119
/* At least one of b.real or b.imag is a NaN */
121120
r.real = r.imag = Py_NAN;
122121
}
122+
123+
/* Recover infinities and zeros that computed as nan+nanj. See e.g.
124+
the C11, Annex G.5.2, routine _Cdivd(). */
125+
if (isnan(r.real) && isnan(r.imag)) {
126+
if ((isinf(a.real) || isinf(a.imag))
127+
&& isfinite(b.real) && isfinite(b.imag))
128+
{
129+
const double x = copysign(isinf(a.real) ? 1.0 : 0.0, a.real);
130+
const double y = copysign(isinf(a.imag) ? 1.0 : 0.0, a.imag);
131+
r.real = Py_INFINITY * (x*b.real + y*b.imag);
132+
r.imag = Py_INFINITY * (y*b.real - x*b.imag);
133+
}
134+
else if ((isinf(abs_breal) || isinf(abs_bimag))
135+
&& isfinite(a.real) && isfinite(a.imag))
136+
{
137+
const double x = copysign(isinf(b.real) ? 1.0 : 0.0, b.real);
138+
const double y = copysign(isinf(b.imag) ? 1.0 : 0.0, b.imag);
139+
r.real = 0.0 * (a.real*x + a.imag*y);
140+
r.imag = 0.0 * (a.imag*x - a.real*y);
141+
}
142+
}
143+
123144
return r;
124145
}
125146
#ifdef _M_ARM64

0 commit comments

Comments
 (0)