I have a file, sum_array.pyx, which is:
# sum_array.pyx
cimport numpy as np
def sum_array(np.ndarray[np.float128_t, ndim=1] arr):
cdef int n = arr.shape[0]
cdef np.float128_t total = 0
cdef int i
for i in range(n):
total += arr[i]
return total
and setup.py which is:
# setup.py
from setuptools import setup
from Cython.Build import cythonize
import numpy as np
setup(
ext_modules=cythonize("sum_array.pyx"),
include_dirs=[np.get_include()]
)
when I run
python setup.py build_ext --inplace
I get:
python setup.py build_ext --inplace
Compiling sum_array.pyx because it changed.
[1/1] Cythonizing sum_array.pyx
/home/user/python/mypython3.10/lib/python3.10/site-packages/Cython/Compiler/Main.py:381: FutureWarning: Cython directive 'language_level' not set, using '3str' for now (Py3). This has changed from earlier releases! File: /home/user/python/sum_array.pyx
tree = Parsing.p_module(s, pxd, full_module_name)
Error compiling Cython file:
------------------------------------------------------------
...
# sum_array.pyx
cimport numpy as np
def sum_array(np.ndarray[np.float128_t, ndim=1] arr):
^
------------------------------------------------------------
sum_array.pyx:4:28: Invalid type.
Traceback (most recent call last):
File "/home/user/python/setup.py", line 7, in <module>
ext_modules=cythonize("sum_array.pyx"),
File "/home/iser/python/mypython3.10/lib/python3.10/site-packages/Cython/Build/Dependencies.py", line 1154, in cythonize
cythonize_one(*args)
File "/home/user/python/mypython3.10/lib/python3.10/site-packages/Cython/Build/Dependencies.py", line 1321, in cythonize_one
raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: sum_array.pyx
What is the right way to do this?
As I said in the comments, using long double
works:
cimport numpy as np
import numpy as np
def f(np.ndarray[long double, ndim=1] arr):
cdef long double sum = 0
for i in range(arr.shape[0]):
sum += arr[i]
return sum
def test_f():
arr = np.ones((1000,), dtype=np.float128)
print(f(arr))
Calling test_f()
prints 1000, as you'd expect.
From the answer I linked in the comments, the name np.float_128
is kind of misleading - it's still a long double
and so (mostly, depending on the system) an 80-bit float. The "128" is really specifying the alignment rather that the level of precision. But this means that specifying the C type as long double
is fine (since it is the same type). What might not work is specifying a C contiguous array too, since that might contradict the alignment requirements of np.float_128
.
Returning an np.float128
:
What you need to avoid is using Cython's default long double
-> Python object conversion, since this converts to a Python float
(actually C double precision).
Instead you need to create a single element Numpy float128
array,
create a Cython typed memoryview of that array, write the result into that and then get the first element.
cdef long double calculated_result = 10.0 # example
py_res = np.empty((1,), dtype=np.float128)
cdef long double[::1] res_view = py_res
res_view[0] = calculated_result
return py_res[0]
There's probably an alternative method involving manually calling the C API function PyArray_Scalar
. The complications there are:
long double
you're passing in, since I suspect it copies the full 128 bits.Implementing that is beyond me right now, but it's an option if you absolutely need it. The memoryview of an array method does work though, so I suggest using that.
use np.longdouble
.
As numpy document says:
np.float96 and np.float128 provide only as much precision as np.longdouble, that is, 80 bits on most x86 machines and 64 bits in standard Windows builds.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With