Repository navigation
Invalid corner cases (resulting in nan+nanj) in complex division #119372
Copy link
Copy link
Closed
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Bug report
Bug description:
Reproducer:
>>> (1+1j)/(cmath.inf+0j) # ok
0j
>>> (1+1j)/cmath.infj # ok
-0j
>>> (1+1j)/(cmath.inf+cmath.infj) # should be 0j, isn't?
(nan+nanj)c.f. MPC:
>>> gmpy2.mpc('(1+1j)')/gmpy2.mpc('(inf+infj)')
mpc('0.0+0.0j')It seems, there are not so much such cases (i.e. when the numerator is finite and the denominator has infinite and infinite/nan components, or if the numerator has infinite components and the denominator has zeros). Following patch (mostly literally taken from the C11 Annex G.5.1 _Cdivd()'s example, except for the denom == 0.0 case) should fix the issue:
diff --git a/Objects/complexobject.c b/Objects/complexobject.c
index b3d57b8337..9041cbb8ae 100644
--- a/Objects/complexobject.c
+++ b/Objects/complexobject.c
@@ -122,6 +122,27 @@ _Py_c_quot(Py_complex a, Py_complex b)
/* At least one of b.real or b.imag is a NaN */
r.real = r.imag = Py_NAN;
}
+
+ /* Recover infinities and zeros that computed as nan+nanj */
+ if (isnan(r.real) && isnan(r.imag)) {
+ if ((isinf(a.real) || isinf(a.imag))
+ && isfinite(b.real) && isfinite(b.imag))
+ {
+ const double x = copysign(isinf(a.real) ? 1.0 : 0.0, a.real);
+ const double y = copysign(isinf(a.imag) ? 1.0 : 0.0, a.imag);
+ r.real = Py_INFINITY * (x*b.real + y*b.imag);
+ r.imag = Py_INFINITY * (y*b.real - x*b.imag);
+ }
+ else if ((fmax(abs_breal, abs_bimag) == Py_INFINITY)
+ && isfinite(a.real) && isfinite(a.imag))
+ {
+ const double x = copysign(isinf(b.real) ? 1.0 : 0.0, b.real);
+ const double y = copysign(isinf(b.imag) ? 1.0 : 0.0, b.imag);
+ r.real = 0.0 * (a.real*x + a.imag*y);
+ r.imag = 0.0 * (a.imag*x - a.real*y);
+ }
+ }
+
return r;
}
Perhaps, we could also clear ending remark in _Py_c_quot() comment:
cpython/Objects/complexobject.c
Lines 91 to 92 in d065edf
| * University). As usual, though, we're still ignoring all IEEE | |
| * endcases. |
I'll prepare a PR, if this issue will be accepted.
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Activity
eendebakpt commentedon May 22, 2024
I can confirm the behavior described above. Some similar cases:
But numpy shows the same behaviour as current cpython main
serhiy-storchaka commentedon May 22, 2024
I agree. If the numerator is finite and the denominator is infinite, the result should be zero. If the numerator is also infinite or contains a NaN, the result should contain a NaN (but not always both component should be NaN). Do you mind to create a PR? Pay attention to the sign of zeros, in some cases it can be clearly defined.
skirpichev commentedon May 22, 2024
Please note that this issue was restricted to true division and
(nan+nanj)output.That's similar to my example: CPython's complex arithmetic has no mixed-mode rules (e.g. for float x complex), so it's an equivalent of
(1+0j)/(cmath.inf + cmath.infj). Should be0j.Seems to be valid:
inf * 1j == (inf+0j)*(0+1j)=(inf*0-0*1)+(inf*1+0*0)j=(nan+infj). You might expectinfjoutput, per C11 Annex G, but this will imply mentioned above mixed-mode rules for complex arithmetic (and special imaginary data type, e.g. for1j).But we have some cases, when CPython math give
(nan+nanj)for multiplication and C code - something different. If this worth an issue - it should be a separate one.an example
I guess, implementation copied from CPython.
@serhiy-storchaka, yes, see above diff.
pythongh-119372: recover inf's and zeros in _Py_c_quot
skirpichev commentedon May 23, 2024
FYI, PR #119457 is ready for review.
pythongh-119372: Recover inf's and zeros in _Py_c_quot/_Py_c_quot_real
pythongh-119372: Recover inf's and zeros in _Py_c_quot/_Py_c_quot_real
pythongh-119372: Recover inf's and zeros in _Py_c_quot/_Py_c_quot_real
gh-119372: Recover inf's and zeros in _Py_c_quot (GH-119457)
pythongh-119372: Recover inf's and zeros in _Py_c_quot (pythonGH-119457)
pythongh-119372: Recover inf's and zeros in _Py_c_quot (pythonGH-119457)
pythongh-119372: Recover inf's and zeros in _Py_c_quot (pythonGH-119457)