如何在Cython中使用128位整数

时间:2014-12-20 16:11:27

标签: c gcc cython long-integer int128

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

print(sizeof(long long))
# prints 8

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

以下不起作用。正在编译仅包含

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

3 个答案:

答案 0 :(得分: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代码中进行强制转换,因为在该过程的该步骤中它实际上不区分它们

答案 1 :(得分:4)

我会把我的两分钱扔到这里。

首先,在其他答案中提出的使用外部typedef的解决方案不仅仅是一种解决方法,这就是Cython docs应该这样做的事情。 见the relevant section。 Quote:“如果头文件使用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。

答案 2 :(得分: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'