Skip to content

Commit 29cb2cc

Browse files
committed
from 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 9bdb8af commit 29cb2cc

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
@@ -154,6 +154,33 @@ def test_truediv(self):
154154
self.assertTrue(isnan(z.real))
155155
self.assertTrue(isnan(z.imag))
156156

157+
self.assertComplexesAreIdentical(complex(INF, 1)/(0.0+1j),
158+
complex(NAN, -INF))
159+
160+
# test recover of infs if numerator has infs and denominator is finite
161+
self.assertComplexesAreIdentical(complex(INF, -INF)/(1+0j),
162+
complex(INF, -INF))
163+
self.assertComplexesAreIdentical(complex(INF, INF)/(0.0+1j),
164+
complex(INF, -INF))
165+
self.assertComplexesAreIdentical(complex(NAN, INF)/complex(2**1000, 2**-1000),
166+
complex(INF, INF))
167+
self.assertComplexesAreIdentical(complex(INF, NAN)/complex(2**1000, 2**-1000),
168+
complex(INF, -INF))
169+
170+
# test recover of zeros if denominator is infinite
171+
self.assertComplexesAreIdentical((1+1j)/complex(INF, INF), (0.0+0j))
172+
self.assertComplexesAreIdentical((1+1j)/complex(INF, -INF), (0.0+0j))
173+
self.assertComplexesAreIdentical((1+1j)/complex(-INF, INF),
174+
complex(0.0, -0.0))
175+
self.assertComplexesAreIdentical((1+1j)/complex(-INF, -INF),
176+
complex(-0.0, 0))
177+
self.assertComplexesAreIdentical((INF+1j)/complex(INF, INF),
178+
complex(NAN, NAN))
179+
self.assertComplexesAreIdentical(complex(1, INF)/complex(INF, INF),
180+
complex(NAN, NAN))
181+
self.assertComplexesAreIdentical(complex(INF, 1)/complex(1, INF),
182+
complex(NAN, NAN))
183+
157184
self.assertEqual((1+1j)/float(2), 0.5+0.5j)
158185
self.assertRaises(TypeError, operator.truediv, None, 1+1j)
159186
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)