2014-12-20 39 views
8

在我的64位計算機上,long long類型有64位。如何在Cython中使用128位整數

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 
+0

@BrettHale我不能只輸入'CDEF __int128_t x = 0的'。它不會編譯。 –

+1

在這種情況下,'cdef'究竟是什麼? –

+2

很明顯,這個問題是關於cython的,儘管它在標題中提到。 –

回答

8

編輯:這不是一個解決辦法了,這是做正確的方式。也請參閱@ 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,第一步通過,和文件生成,然後gcc編譯也會通過。

現在,如果您打開文件helloworld.c,則可以檢查變量foo實際上是否被聲明爲int128

請謹慎使用此解決方法。特別是,例如,如果您將int128指定給int64(例如,因爲在該過程的該步驟中它實際上不區分它們),則可能發生cython不需要在C代碼中強制轉換。

+0

雖然它確實編譯,我似乎無法存儲多於64位的數它。代碼'cdef int128 bar = 1 << 64 \ n print(bar)'打印'0'。另一方面,'sizeof(int128)'說'16',就像我們想要的那樣。 –

+0

我也嘗試了簡單的C++代碼轉換,它將大於'int64'的'int128'轉換爲'0'。我想這不是'cython'的限制,但至少在我的機器上,'__int128_t'有內在的錯誤。 – gg349

+0

類型'__int128'在C中對我很好(除了許多函數無法處理它們)。 'ctypedef unsigned long long int128'這行是不是意味着你把'int128'設置爲'unsigned long long'的別名? –

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' 
4

我會扔我兩分錢在這裏運行。

首先,其他答案中提出的使用外部typedef的解決方案不僅僅是一種解決方法,也是Cython文檔所說的應該這樣做的方式。 見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