如何在Cython中使用128位整数

问题描述 投票:9回答:3

在我的64位计算机上,long long类型有64位。

print(sizeof(long long))
# prints 8

我需要使用128位整数,幸运的是GCC supports these。我怎样才能在Cython中使用它们?

以下不起作用。编译包含just的foo.pyx

cdef __int128_t x = 0

产量

$ cython foo.pyx 

Error compiling Cython file:
------------------------------------------------------------
...

cdef __int128_t x = 0
    ^
------------------------------------------------------------

foo.pyx:2:5: '__int128_t' is not a type identifier
c gcc cython long-integer int128
3个回答
9
投票

编辑:这不再是一种解决方法,这是正确的方法。另请参阅@ IanH的答案。

现在,你遇到的问题是,cython无法识别你的类型,而gcc则有。所以我们可以尝试欺骗cython

文件helloworld.pyx

cdef extern from "header_int128.h":
    # this is WRONG, as this would be a int64. it is here
    # just to let cython pass the first step, which is generating
    # the .c file.
    ctypedef unsigned long long int128

print "hello world"

cpdef int foo():
    cdef int128 foo = 4
    return 32

文件header_int128.h

typedef __int128_t int128;

文件setup.py

from distutils.core import setup
from Cython.Build import cythonize

setup(ext_modules = cythonize("helloworld.pyx"))

现在,在我的机器上,当我运行python setup.py build_ext --inplace时,第一步通过,生成文件helloworld.c,然后gcc编译也通过。

现在,如果你打开文件helloworld.c,你可以检查你的变量foo实际上是否被声明为int128

使用此变通方法时要非常小心。特别是,如果你将int128分配给int64,可能会发生cython不需要C代码中的强制转换,因为在该过程的那一步它实际上不区分它们。


4
投票

我会在这里扔两分钱。

首先,在其他答案中提出的使用外部typedef的解决方案不仅仅是一种解决方法,这就是Cython文档说这样的事情应该完成的方式。见the relevant section。引用:“如果头文件使用typedef名称(例如word)来引用数据类型的平台相关风格,则需要相应的ctypedef语句,但是您不需要完全匹配类型,只需使用右侧的内容一般类型(int,float等)。例如ctypedef int word无论word的实际大小是什么都可以正常工作(前提是头文件正确定义了它。)Python类型的转换(如果有的话)也将用于此新型。”

此外,没有必要为您已经包含在其他地方的类型创建一个带有typedef的头文件。就这样做吧

cdef extern from *:
    ctypedef int int128 "__int128_t"

或者,如果您想在Cython中保持名称与在C中保持相同,

cdef extern from *:
    ctypedef int __int128_t

这是一个测试,以证明这是有效的。如果128位算术正在工作,a > 1和a可表示为64位整数,则第一个函数将再次打印相同的数字。如果不是,整数溢出应该使它打印0.第二个函数显示如果使用64位算术会发生什么。

Cython文件

# cython: cdivision = True

cdef extern from *:
    ctypedef int int128 "__int128_t"

def myfunc(long long a):
    cdef int128 i = a
    # set c to be the largest positive integer possible for a signed 64 bit integer
    cdef long long c = 0x7fffffffffffffff
    i *= c
    cdef long long b = i / c
    print b

def myfunc_bad(long long a):
    cdef long long i = a
    # set c to be the largest positive integer possible for a signed 64 bit integer
    cdef long long c = 0x7fffffffffffffff
    i *= c
    cdef long long b = i / c
    print b

在Python中,在导入了两个函数后,myfunc(12321)打印正确的值,而myfunc_bad(12321)打印0。


3
投票

以下是使用@Giulio Ghirardo提出的黑客的示例。

文件cbitset.px包含:

typedef unsigned __int128 bitset;

文件bitset.pyx包含:

from libc.stdlib cimport malloc
from libc.stdio cimport printf

cdef extern from "cbitset.h":
    ctypedef unsigned long long bitset

cdef char* bitset_tostring(bitset n):
    cdef char* bitstring = <char*>malloc(8 * sizeof(bitset) * sizeof(char) + 1)
    cdef int i = 0
    while n:
        if (n & <bitset>1):
            bitstring[i] = '1'
        else:
            bitstring[i] = '0'

        n >>= <bitset>1
        i += 1
    bitstring[i] = '\0'
    return bitstring

cdef void print_bitset(bitset n):
    printf("%s\n", bitset_tostring(n))

文件main.pyx包含:

from bitset cimport print_bitset

cdef extern from "cbitset.h":
    ctypedef unsigned long long bitset

# x contains a number consisting of more than 64 1's
cdef bitset x = (<bitset>1 << 70) - 1

print_bitset(x)
# 1111111111111111111111111111111111111111111111111111111111111111111111

文件setup.py包含:

from distutils.core import setup
from Cython.Build import cythonize

setup(
    name="My app that used 128 bit ints",
    ext_modules=cythonize('main.pyx')
)

使用命令编译它

python3 setup.py build_ext --inplace

并使用该命令运行

python3 -c 'import main'
© www.soinside.com 2019 - 2024. All rights reserved.