2009-09-03 3 views

risposta

13

Stai lavorando in un linguaggio di scripting di alto livello; per natura, i tipi di dati nativi del sistema su cui stai lavorando non sono visibili. Non è possibile eseguire il cast su un int firmato nativo con codice come questo.

Se si sa che si desidera che il valore convertito in un intero con segno a 32 bit - indipendentemente dalla piattaforma - si può solo fare la conversione con la semplice matematica:

iv = 0xDEADBEEF 
if(iv & 0x80000000): 
    iv = -0x100000000 + iv 
+0

Questo non è accurato: tutti i calcoli che si adattano alla parola della macchina vengono eseguiti in quel livello. Ad esempio, ~ 1 == -2. Quindi sto cercando un modo per forzare il risultato in tipo int (che non è ciò che int() fa, apparentemente). –

+0

~ 1 == -2 in Python su * tutti * i sistemi. Se questo non è il caso sul sistema nativo, quindi un'implementazione Python deve emularlo. (Vedi http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy.) L'unica volta che la dimensione della parola nativa può perdere in Python è quando la dimensione di int è maggiore di 32-bit , il che significa che si effettua la transizione da Int a molto più tardi. –

+0

Non è possibile forzare Python a convertire un valore long in un int quando il valore non si adatta a un int; non puoi scrivere codice Python come se fossi in C.Se si desidera convertire il valore int intatto a 32 bit da 0xFFFFFFFF al numero intero a 32 bit con complemento firmato e rappresentato dalla stessa stringa di bit, allora ho fornito il codice. Altrimenti, non sono sicuro di cosa stai cercando di fare. –

5

Si può usare struct libreria per convertire valori del genere. E 'brutto, ma funziona:

from struct import pack, unpack 
signed = unpack('l', pack('L', lv & 0xffffffff))[0] 
+0

Funziona su macchine a 32 bit e non funziona a 64 bit – nkrkv

+3

Questo può essere reso portatile se si utilizza uno dei caratteri di ordine/dimensione/allineamento. Ad esempio, usa ''= l'' e '' = L'' come stringhe di formato. – SamB

19
import ctypes 

number = lv & 0xFFFFFFFF 

signed_number = ctypes.c_long(number).value 
6

Posso suggerire questo:

def getSignedNumber(number, bitLength): 
    mask = (2 ** bitLength) - 1 
    if number & (1 << (bitLength - 1)): 
     return number | ~mask 
    else: 
     return number & mask 

print iv, '->', getSignedNumber(iv, 32) 
5

In sostanza, il problema è quello di firmare estendere da 32 bit a ... un numero infinito di bit, perché Python ha numeri interi arbitrariamente grandi. Normalmente, l'estensione del segno viene eseguita automaticamente dalle istruzioni della CPU durante il casting, quindi è interessante che sia più difficile in Python di quanto non lo sarebbe, ad esempio C.

Giocando in giro, ho trovato qualcosa di simile alla funzione di BreizhGatch, ma che non richiede una dichiarazione condizionale. n & 0x80000000 estrae il bit di segno a 32 bit; quindi, lo - mantiene la stessa rappresentazione a 32 bit ma firma-estende; infine, i bit di segno esteso sono impostati su n.

def toSigned32(n): 
    n = n & 0xffffffff 
    return n | (-(n & 0x80000000)) 

Bit Twiddling Hacks suggerisce un'altra soluzione che forse risulterà più in generale. n^0x80000000 capta il bit di segno a 32 bit; quindi - 0x80000000 firmerà-estenderà il bit opposto. Un altro modo di pensarci è che inizialmente i numeri negativi sono al di sopra dei numeri positivi (separati da 0x80000000); lo ^ scambia le loro posizioni; poi i turni - numeri negativi al di sotto 0.

def toSigned32(n): 
    n = n & 0xffffffff 
    return (n^0x80000000) - 0x80000000 
+0

+1 per non utilizzare i condizionali. Probabilmente non fa una grande differenza in Python, ma sicuramente sembra più soddisfacente. – zneak

0

Una soluzione rapida e sporca (x non è mai superiore a 32 bit nel mio caso).

if x > 0x7fffffff: 
    x = x - 4294967296