【问题标题】:How to use 128 bit integers in Cython如何在 Cython 中使用 128 位整数
【发布时间】:2015-02-19 07:56:53
【问题描述】:

在我的 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

【问题讨论】:

  • @BrettHale 我不能只输入cdef __int128_t x = 0。它无法编译。
  • 在这种情况下,cdef 到底是什么?
  • 显然不清楚这个问题是关于 cython 的,尽管它在标题中说。
  • @iharob 出于同样的原因,一个也不会有 64 位 int,因为 python 也没有这些。在 python 中,整数是任意大小的。
  • @iharob 问题不是关于 python,而是关于 cython。您可以在 cython 中使用各种 C 结构/类型,而无需在 python 中公开/使用它们。

标签: c gcc cython long-integer int128


【解决方案1】:

编辑:这不再是一种解决方法,这是正确的方法。另请参阅@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 代码中进行强制转换,因为在该过程的该步骤中,它实际上并没有区分它们。

【讨论】:

  • 虽然它确实可以编译,但我似乎无法在其中存储超过 64 位的数量。代码cdef int128 bar = 1 << 64 \n print(bar) 打印0。另一方面,sizeof(int128)16,就像我们想要的那样。
  • 我也尝试过在纯 C++ 代码中进行简单转换,它把大于int64int128 转换为0。我想这不是cython 的限制,而是__int128_t 本身就有问题,至少在我的机器上是这样。
  • __int128 类型在 C 中对我来说很好用(除了许多函数无法处理它们)。 ctypedef unsigned long long int128 这行不是意味着您将int128 设置为unsigned long long 的别名吗?
  • 是的,但这只是一个占位符,让 cython 进程成功完成。当您编译代码时,编译器会包含真正的头文件,并且不会读取该 ctypedef。
  • 我发布了一个包含您的解决方案示例的答案。感谢您的帮助!
【解决方案2】:

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

首先,其他答案中提出的使用外部 typedef 的解决方案不仅仅是一种解决方法,这也是 Cython 文档所说的应该做这样的事情的方式。 见the relevant section。 引用:“如果头文件使用诸如 word 之类的 typedef 名称来引用与平台相关的数字类型,则需要相应的 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: 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 提出的hack 的示例。

    文件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'
    

    【讨论】:

      猜你喜欢
      • 2013-11-07
      • 2015-10-06
      • 2011-03-02
      • 1970-01-01
      • 2015-11-28
      • 2016-10-11
      • 2013-08-28
      • 2013-04-11
      相关资源
      最近更新 更多