2014-12-20 17 views
8

Sul mio computer a 64 bit il tipo long long ha 64 bit.Come utilizzare interi 128 bit in Cython

print(sizeof(long long)) 
# prints 8 

Ho bisogno di usare interi a 128 bit e fortunatamente GCC supports these. Come posso usarli in Cython?

Quanto segue non funziona. Compilazione foo.pyx contenente solo

cdef __int128_t x = 0 

produce

$ 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 posso non basta digitare 'cdef __int128_t x = 0 '. Non viene compilato. –

+1

Che cosa è esattamente 'cdef' in questo contesto? –

+2

Apparentemente non è chiaro che questa domanda riguardi cython, anche se nel titolo si dice. –

risposta

8

EDIT: questa non è più una soluzione, questo è il modo giusto per farlo. Fare riferimento anche alla risposta di @ IanH.

Ora, il problema è che cython non riconosce il tuo tipo, mentre lo fa gcc. Quindi possiamo provare a ingannare cython.

File 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 

File header_int128.h:

typedef __int128_t int128; 

File setup.py:

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

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

Ora, sulla mia macchina, quando corro python setup.py build_ext --inplace, il primo passo passa, e il file helloworld.c viene generato e quindi passa anche la compilazione gcc.

Ora se si apre il file helloworld.c, è possibile verificare che la variabile foo sia effettivamente dichiarata come int128.

Fare molta attenzione quando si utilizza questa soluzione alternativa. In particolare, può accadere che cython non richieda un cast nel codice C se si assegna un int128 ad un int64 per esempio, perché in quel passo del processo in realtà non fa distinzioni tra di essi.

+0

Mentre compila davvero, non riesco a memorizzare un numero di oltre 64 bit in esso. Il codice 'cdef int128 bar = 1 << 64 \ n print (bar)' stampa '0'. D'altra parte, 'sizeof (int128)' dice '16', come vogliamo. –

+0

Ho anche provato il cast semplice in un solo codice C++, e ha lanciato un 'int128' più grande di un' int64' in '0'. Immagino che questa non sia una limitazione di 'cython', ma qualcosa di intrinsecamente sbagliato con' __int128_t', almeno sulla mia macchina. – gg349

+0

Il tipo '__int128' funziona bene per me in C (a parte il fatto che molte funzioni non possono gestirli). La riga 'ctypedef unsigned long long int128' non significa che imposti' int128' come alias per 'unsigned long long'? –

3

Ecco un esempio per l'utilizzo dell'hack proposto da @Giulio Ghirardo.

Il file contiene cbitset.px:

typedef unsigned __int128 bitset; 

Il file contiene 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)) 

Il file contiene 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 

Il file contiene setup.py:

0.123.516,41 mila
from distutils.core import setup 
from Cython.Build import cythonize 

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

compilare questo usando il comando

python3 setup.py build_ext --inplace 

ed eseguire utilizzando il comando

python3 -c 'import main' 
4

mi butto i miei due centesimi in qui.

In primo luogo, la soluzione proposta nelle altre risposte che dice di usare un typedef esterno non è solo una soluzione, cioè il modo in cui i documenti Cython dicono che cose come questa dovrebbero essere fatte. Vedere the relevant section. Preventivo: "Se il file di intestazione utilizza nomi typedef come word per fare riferimento a tipi di tipi numerici dipendenti dalla piattaforma, sarà necessaria un'istruzione ctypedef corrispondente, ma non è necessario che corrisponda esattamente al tipo, basta usare qualcosa del tipo giusto tipo generale (int, float, ecc.) Ad esempio, ctypedef int word funzionerà correttamente qualunque sia la dimensione effettiva di un word (se il file di intestazione lo definisce correttamente). La conversione da e verso i tipi Python, se presenti, verrà utilizzata anche per questo nuovo tipo. "

Inoltre, non è necessario creare effettivamente un file di intestazione con un typedef per un tipo che hai già incluso da qualche altra parte lungo la strada. solo fare questo

cdef extern from *: 
    ctypedef int int128 "__int128_t" 

Oppure, se avete voglia di mantenere il nome lo stesso in Cython come in C,

cdef extern from *: 
    ctypedef int __int128_t 

Ecco un test per dimostrare che questo è in funzione. Se l'aritmetica a 128 bit funziona, a > 1, e a è rappresentabile come un numero intero a 64 bit, la prima funzione stamperà nuovamente lo stesso numero. Se non lo è, l'overflow dei numeri interi dovrebbe causarne la stampa 0. La seconda funzione mostra cosa succede se si utilizza l'aritmetica a 64 bit.

Cython il file

# 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 

In Python, dopo che entrambe le funzioni sono state importate, myfunc(12321) stampe il valore corretto, mentre myfunc_bad(12321) stampe 0.

Problemi correlati