A.J. Uppal
A.J. Uppal

Reputation: 19264

Why does exponentiating a numpy.float64 return nan?

When I am raising a negative numpy.float64 to an exponent, I am receiving an nan. Why is the complex math not supported? Is the only workaround a cast to float?

>>> from numpy import float64, power
>>> r = float64(-12025.433836763057)
>>> p = 0.74
>>> r**p
nan
>>> power(r, p)
__main__:1: RuntimeWarning: invalid value encountered in power
nan
>>> float(r)**p
(-715.6124638577838+762.049873596874j)
>>> 

A suggested duplicate has a similar question, with an answer stating that this is a bug in numpy. Is this the end of the road?

Upvotes: 2

Views: 1076

Answers (2)

Andrew L
Andrew L

Reputation: 1224

Behind the scenes, python is casting that float(r)**p to return a 'complex' type.

The numpy power function is intended to work with numpy array_like structures where all the items are the same size and stored in a contiguous block of memory and its return type is inferred from its arguments.

If you are expecting complex numbers, the best approach would be to use the complex64 or complex128 types. These require more memory because each complex type consists of a real and imaginary component. So complex64 would consist of two float32 numbers and complex128 would consist of two float64 numbers.

>>> import numpy as np
>>> r = np.complex128(-12025.433836763057)
>>> p = 0.74
>>> np.power(r, p)
(-715.6124638577835+762.0498735968736j)

You can also cast directly in the power function:

>>> import numpy as np
>>> r = np.float64(-12025.433836763057)
>>> p = 0.74
>>> np.power(r.astype(np.complex128), p)
(-715.6124638577835+762.0498735968736j)

But the simplest approach might be to just alter the power function's return type to expect a complex number:

>>> import numpy as np
>>> r = np.float64(-12025.433836763057)
>>> p = 0.74
>>> np.power(r, p, dtype=np.complex128)
(-715.6124638577835+762.0498735968736j)

What's interesting is that numpy does normally allow casting types from float64 to complex as long as they keep the same level of precision. However it doesn't seem to allow implicit casting of any ufunc function return types even if the casting='same_kind' kwarg is overridden.

>>> np.can_cast(np.float64, complex)
True
>>> np.can_cast(np.float64, np.complex64)
False  
>>> np.can_cast(np.float64, np.complex128)
True

According to the docs, if scalar arguments are passed to the ufunc (as opposed to arrays) it uses the logic in np.result_type and np.promote_types to determine the ufunc's return type.

https://docs.scipy.org/doc/numpy/reference/ufuncs.html

https://docs.scipy.org/doc/numpy/reference/generated/numpy.result_type.html#numpy.result_type

>>> np.result_type(r, p)
dtype('float64')

Upvotes: 1

Sohrab T
Sohrab T

Reputation: 653

The numpy.float64 is probably represented under the hood by a C language data structure which is strongly typed. The python float is pythonic, and so plays nicely with the complex number handling that python provides.

See: https://docs.scipy.org/doc/numpy/reference/c-api.dtype.html

Upvotes: 1

Related Questions