Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use float128 in Cython 3?

Tags:

python

cython

  • Cython version: 3.0.10
  • Python 3.10.12

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?

like image 610
graffe Avatar asked Oct 13 '25 01:10

graffe


2 Answers

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:

  1. I believ that function steals a reference to the dtype (but doesn't document it), so you need to account for that.
  2. You probably also need to account for the alignment of the 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.

like image 142
DavidW Avatar answered Oct 14 '25 18:10

DavidW


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.

like image 40
Arthur Avatar answered Oct 14 '25 16:10

Arthur