Skip to content

Commit 99d1b8c

Browse files
committed
pythongh-119372: Recover inf's and zeros in _Py_c_quot/_Py_c_quot_real
In some cases, previosly computed as (nan+nanj), we could recover meaningful component values in the result, see e.g. the C11, Annex G.5.2, routine _Cdivd(): >>> from cmath import inf, infj, nanj >>> (1+1j)/(inf+infj) # was (nan+nanj) 0j >>> (inf+nanj)/complex(2**1000, 2**-1000) (inf-infj)
1 parent 57841e1 commit 99d1b8c

File tree

2 files changed

+65
-2
lines changed

2 files changed

+65
-2
lines changed

Lib/test/test_complex.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,33 @@ def test_truediv(self):
142142
self.assertTrue(isnan(z.real))
143143
self.assertTrue(isnan(z.imag))
144144

145+
self.assertComplexesAreIdentical(complex(INF, 1)/(0.0+1j),
146+
complex(NAN, -INF))
147+
148+
# test recover of infs if numerator has infs and denominator is finite
149+
self.assertComplexesAreIdentical(complex(INF, -INF)/(1+0j),
150+
complex(INF, -INF))
151+
self.assertComplexesAreIdentical(complex(INF, INF)/(0.0+1j),
152+
complex(INF, -INF))
153+
self.assertComplexesAreIdentical(complex(NAN, INF)/complex(2**1000, 2**-1000),
154+
complex(INF, INF))
155+
self.assertComplexesAreIdentical(complex(INF, NAN)/complex(2**1000, 2**-1000),
156+
complex(INF, -INF))
157+
158+
# test recover of zeros if denominator is infinite
159+
self.assertComplexesAreIdentical((1+1j)/complex(INF, INF), (0.0+0j))
160+
self.assertComplexesAreIdentical((1+1j)/complex(INF, -INF), (0.0+0j))
161+
self.assertComplexesAreIdentical((1+1j)/complex(-INF, INF),
162+
complex(0.0, -0.0))
163+
self.assertComplexesAreIdentical((1+1j)/complex(-INF, -INF),
164+
complex(-0.0, 0))
165+
self.assertComplexesAreIdentical((INF+1j)/complex(INF, INF),
166+
complex(NAN, NAN))
167+
self.assertComplexesAreIdentical(complex(1, INF)/complex(INF, INF),
168+
complex(NAN, NAN))
169+
self.assertComplexesAreIdentical(complex(INF, 1)/complex(1, INF),
170+
complex(NAN, NAN))
171+
145172
self.assertEqual((1+1j)/float(2), 0.5+0.5j)
146173
self.assertRaises(TypeError, operator.truediv, None, 1+1j)
147174
self.assertRaises(TypeError, operator.truediv, 1+1j, None)

Objects/complexobject.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ _Py_c_quot(Py_complex a, Py_complex b)
9090
* numerators and denominator by whichever of {b.real, b.imag} has
9191
* larger magnitude. The earliest reference I found was to CACM
9292
* Algorithm 116 (Complex Division, Robert L. Smith, Stanford
93-
* University). As usual, though, we're still ignoring all IEEE
94-
* endcases.
93+
* University).
9594
*/
9695
Py_complex r; /* the result */
9796
const double abs_breal = b.real < 0 ? -b.real : b.real;
@@ -122,6 +121,28 @@ _Py_c_quot(Py_complex a, Py_complex b)
122121
/* At least one of b.real or b.imag is a NaN */
123122
r.real = r.imag = Py_NAN;
124123
}
124+
125+
/* Recover infinities and zeros that computed as nan+nanj. See e.g.
126+
the C11, Annex G.5.2, routine _Cdivd(). */
127+
if (isnan(r.real) && isnan(r.imag)) {
128+
if ((isinf(a.real) || isinf(a.imag))
129+
&& isfinite(b.real) && isfinite(b.imag))
130+
{
131+
const double x = copysign(isinf(a.real) ? 1.0 : 0.0, a.real);
132+
const double y = copysign(isinf(a.imag) ? 1.0 : 0.0, a.imag);
133+
r.real = Py_INFINITY * (x*b.real + y*b.imag);
134+
r.imag = Py_INFINITY * (y*b.real - x*b.imag);
135+
}
136+
else if ((fmax(abs_breal, abs_bimag) == Py_INFINITY)
137+
&& isfinite(a.real) && isfinite(a.imag))
138+
{
139+
const double x = copysign(isinf(b.real) ? 1.0 : 0.0, b.real);
140+
const double y = copysign(isinf(b.imag) ? 1.0 : 0.0, b.imag);
141+
r.real = 0.0 * (a.real*x + a.imag*y);
142+
r.imag = 0.0 * (a.imag*x - a.real*y);
143+
}
144+
}
145+
125146
return r;
126147
}
127148

@@ -155,6 +176,21 @@ _Py_c_quot_real(double a, Py_complex b)
155176
else {
156177
r.real = r.imag = Py_NAN;
157178
}
179+
180+
if (isnan(r.real) && isnan(r.imag)) {
181+
if (isinf(a) && isfinite(b.real) && isfinite(b.imag)) {
182+
const double x = copysign(1.0, a);
183+
r.real = Py_INFINITY * (x*b.real);
184+
r.imag = Py_INFINITY * (-x*b.imag);
185+
}
186+
else if ((fmax(abs_breal, abs_bimag) == Py_INFINITY) && isfinite(a)) {
187+
const double x = copysign(isinf(b.real) ? 1.0 : 0.0, b.real);
188+
const double y = copysign(isinf(b.imag) ? 1.0 : 0.0, b.imag);
189+
r.real = 0.0 * (a*x);
190+
r.imag = 0.0 * (-a*y);
191+
}
192+
}
193+
158194
return r;
159195
}
160196
#ifdef _M_ARM64

0 commit comments

Comments
 (0)