2012-04-02 14 views
5

Come si legge nel titolo della domanda, l'assegnazione di 2^31 a una variabile intera a 32 bit con segno e senza segno fornisce un risultato imprevisto.Risultato strano dopo l'assegnazione di 2^31 a una variabile intera a 32 bit con segno e senza segno

Ecco il programma corto (in C++), che ho fatto per vedere cosa sta succedendo:

#include <cstdio> 
using namespace std; 

int main() 
{ 
    unsigned long long n = 1<<31; 
    long long n2 = 1<<31; // this works as expected 
    printf("%llu\n",n); 
    printf("%lld\n",n2); 
    printf("size of ULL: %d, size of LL: %d\n", sizeof(unsigned long long), sizeof(long long)); 
    return 0; 
} 

ecco l'output:

MyPC/# c++ test.cpp -o test 
MyPC/# ./test 
18446744071562067968  <- Should be 2^31 right? 
-2147483648    <- This is correct (-2^31 because of the sign bit) 
size of ULL: 8, size of LL: 8 

Ho poi aggiunto un'altra funzione p(), ad essa :

void p() 
{ 
    unsigned long long n = 1<<32; // since n is 8 bytes, this should be legal for any integer from 32 to 63 
    printf("%llu\n",n); 
} 

In fase di compilazione e esecuzione, questo è ciò che confonde d ancora di più:

MyPC/# c++ test.cpp -o test 
test.cpp: In function ‘void p()’: 
test.cpp:6:28: warning: left shift count >= width of type [enabled by default] 
MyPC/# ./test 
0 
MyPC/

Perché il compilatore dovrebbe lamentarsi del numero di turni a sinistra troppo grande? sizeof(unsigned long long) restituisce 8, quindi non significa che 2^63-1 è il valore massimo per quel tipo di dati?

Mi ha colpito che forse n * 2 e n < < 1, non si comportano sempre allo stesso modo, così ho provato questo:

void s() 
{ 
    unsigned long long n = 1; 
    for(int a=0;a<63;a++) n = n*2; 
    printf("%llu\n",n); 
} 

Questo dà il valore corretto di 2^63 come l'output che è 9223372036854775808 (l'ho verificato usando python). Ma cosa c'è di sbagliato nel fare una merda sinistra?

Uno spostamento aritmetico sinistro da n equivale a moltiplicare per 2 n (il cui valore non trabocchi)

- Wikipedia

Il valore non è traboccante , apparirà solo un segno meno poiché il valore è 2^63 (tutti i bit sono impostati).

Non riesco ancora a capire cosa sta succedendo con il turno di sinistra, qualcuno può spiegarlo per favore?

PS: Questo programma è stato eseguito su una zecca a 32 bit del sistema con Linux (se che aiuta)

+0

Questo dovrebbe essere 'unsigned long long n = 1ULL << 31;' – kirilloid

+0

dio! è stato così facile ?! Perché non ci ho pensato. comunque, sì 1ULL << 31 funziona. quindi grazie! – Rushil

risposta

10

Su questa linea:

unsigned long long n = 1<<32; 

Il problema è che il letterale 1 è di tipo int - che è probabilmente solo 32 bit. Quindi il turno lo spingerà fuori dai limiti.

Solo perché si sta memorizzando in un tipo di dati più grande non significa che tutto nell'espressione viene eseguito con dimensioni maggiori.

Quindi, per correggerlo, è necessario o lanciare verso l'alto o renderlo un unsigned long long letterale:

unsigned long long n = (unsigned long long)1 << 32; 
unsigned long long n = 1ULL << 32; 
+0

Per quanto riguarda l'ultimo suggerimento: ** per favore ** utilizzare i cappucci per i designatori del tipo. Con molti tipi di carattere, distinguere tra '1ll' e' 111' può essere difficile, se non impossibile; '1LL' è chiaro e non ambiguo (e non c'è il suffisso' O' per creare problemi con '0'). –

+0

Suggerimento preso. :) – Mysticial

5

La ragione 1 << 32 non riesce è perché 1 non si ha il giusto tipo (è int).Il compilatore non esegue alcuna magia di conversione prima che l'assegnazione stessa avvenga effettivamente, quindi 1 << 32 viene valutato utilizzando l'aritmetica int, fornendo un avviso su un overflow.

Prova utilizzando 1LL o 1ULL invece che hanno rispettivamente il tipo long long e unsigned long long.

3

La linea

unsigned long long n = 1<<32; 

provoca un overflow, perché il letterale 1 è di tipo int, così 1 << 32 è anche un int, che è di 32 bit in molti casi.

La linea

unsigned long long n = 1<<31; 

fuoriescano anche, per lo stesso motivo. Notare che 1 è di tipo signed int, quindi ha solo 31 bit per il valore e 1 bit per il segno. Pertanto, quando si sposta 1 << 31, i bit di valore vengono espulsi, risultando in -2147483648, che viene quindi convertito in un long lungo non firmato, ovvero 18446744071562067968. Puoi verificarlo nel debugger, se controlli le variabili e le converti.

in modo da utilizzare

unsigned long long n = 1ULL << 31;