Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Reciprocal of complex 0 and 1 / numpy.array(0+0j) give different results #17425

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kurtamohler opened this issue Oct 2, 2020 · 6 comments
Open

Comments

@kurtamohler
Copy link

numpy.reciprocal(a) and 1 / a give different results with a = numpy.array(0 + 0j). reciprocal gives nan+nanj, and 1 / a gives inf+nanj.

It seems like neither of these results is correct, but I could certainly be wrong. I feel like the result in both cases should be inf+0j, since we get inf when we use real numbers: 1 / numpy.array(0.0) and numpy.reciprocal(numpy.array(0.0)).

Reproducing code example:

>>> import numpy

>>> a = numpy.array(0 + 0j)
__main__:1: RuntimeWarning: invalid value encountered in reciprocal
>>> numpy.reciprocal(a)
(nan+nanj)
>>> 1 / a
(inf+nanj)

>>> 1 / numpy.array(0.0)
inf
>>> numpy.reciprocal(numpy.array(0.0))
__main__:1: RuntimeWarning: divide by zero encountered in reciprocal
inf

NumPy/Python version information:

>>> print(numpy.__version__, sys.version)
1.18.1 3.7.4 (default, Aug 13 2019, 20:35:49) 
[GCC 7.3.0]

OS

Distributor ID: Ubuntu
Description: Ubuntu 18.04.4 LTS
Release: 18.04
Codename: bionic

@eric-wieser
Copy link
Member

I feel like the result in both cases should be inf+0j,

What does the IEEE-754 spec say is the correct result?

@kurtamohler
Copy link
Author

I'm not sure what the spec itself says--evidently I'd have to pay for it to find out. I don't see any mention of complex numbers on the wikipedia page, so maybe it's not covered: https://en.wikipedia.org/wiki/IEEE_754

For what it's worth, my version of g++ claims to support IEEE-754 for floats, and I get 1.0f / 0.0f = inf, which agrees with numpy behavior in the case of real numbers.

@eric-wieser
Copy link
Member

eric-wieser commented Oct 2, 2020

Right, my question is about what g++ does for complex division. The C99 spec seems to say:

if the first operand is a nonzero finite number or an infinity and the second operand is a zero, then the result of the / operator is an infinity.

and

A complex or imaginary value with at least one infinite part is regarded as an infinity
(even if its other part is a NaN)

which suggests that the behavior of 1 / a is correct, and reciprocal diverges from the standard.

The implementation is here:

NPY_NO_EXPORT void
@TYPE@_reciprocal(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data))
{
UNARY_LOOP {
const @ftype@ in1r = ((@ftype@ *)ip1)[0];
const @ftype@ in1i = ((@ftype@ *)ip1)[1];
if (npy_fabs@c@(in1i) <= npy_fabs@c@(in1r)) {
const @ftype@ r = in1i/in1r;
const @ftype@ d = in1r + in1i*r;
((@ftype@ *)op1)[0] = 1/d;
((@ftype@ *)op1)[1] = -r/d;
} else {
const @ftype@ r = in1r/in1i;
const @ftype@ d = in1r*r + in1i;
((@ftype@ *)op1)[0] = r/d;
((@ftype@ *)op1)[1] = -1/d;
}
}
}

@pv
Copy link
Member

pv commented Oct 2, 2020

C99 Annex G.5.1.4: the result from np.reciprocal is wrong, and the result from 1/np.complex_(0) is correct. One should have assert np.isinf(np.reciprocal(0+0j)), but it's not specified which complex infinity is returned (and inf+nanj is an infinity).

There's also something else strange here:

>>> z = np.complex_(0+0j)
>>> 1/z
(inf+nanj)
>>> 1/(1/z)
(nan+nanj)

Python doesn't consider (inf, nan) as an infinity in division:

>>> 1/complex(float("inf"), float("nan"))
(nan+nanj)

so it seems there's deviation from C99 spec also there. (In C99, 1/(1/(0+0*I)) appears to evaluate to zero.)

@kurtamohler
Copy link
Author

Ah, that makes sense. Thanks! I can try to make a PR to change reciprocal to match 1 / a this weekend.

kurtamohler added a commit to kurtamohler/numpy that referenced this issue Oct 4, 2020
This change makes `np.reciprocal(0+0j)` return the same result as
`1 / np.array(0+0j)`. See numpy#17425
kurtamohler added a commit to kurtamohler/numpy that referenced this issue Aug 1, 2022
This change makes `np.reciprocal(0+0j)` return the same result as
`1 / np.array(0+0j)`. See numpy#17425
kurtamohler added a commit to kurtamohler/numpy that referenced this issue Aug 1, 2022
This change makes `np.reciprocal(0+0j)` return the same result as
`1 / np.array(0+0j)`. See numpy#17425
@skirpichev
Copy link

Python doesn't consider (inf, nan) as an infinity in division

@pv, now it's a bug: python/cpython#119372. Here is a corresponding NumPy issue: #26560

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants