2013-10-28 17 views
30

Devo essere assolutamente pazzo qui, ma il gcc 4.7.3 sulla mia macchina sta dando il risultato più assurdo. Ecco il codice esatto che sto testando:Il giusto spostamento aritmetico dà risultati fasulli?

#include <iostream> 

using namespace std; 

int main(){ 
    unsigned int b = 100000; 
    cout << (b>>b) << endl; 
    b = b >> b; 
    cout << b << endl; 
    b >>= b; 
    cout << b << endl; 
    return 0; 
} 

Ora, qualsiasi numero che è di destra spostato da solo dovrebbe comportare (n/(2^n) == 0 con intero divario, n>1, e positiva/unsigned) , ma in qualche modo ecco la mia uscita:

100000 
100000 
100000 

Sono pazzo? Cosa potrebbe succedere?

+0

@ShafikYaghmour: Ciò presuppone il compilatore persino i molestatori che stanno mettendo un'istruzione. È proprio nel suo diritto rifiutare questo programma. – MSalters

+0

@MSalters, in questo momento stiamo entrando nel compilatore/piattaforma/versione specifica ma per le versioni correnti e recenti questo è il caso e, come ho già affermato, non è definito in modo da essere chiaramente da soli, 'gcc' sembra solo produce un 'shr' quando si usa' -O0'. –

+0

@ShafikYaghmour: Intel è solo una delle tante piattaforme supportate da gcc e ha diverse fasi di ottimizzazione. Un trucco comune nell'ottimizzazione consiste nel dire "Questo valore può essere compreso tra 0 e 31 perché è utilizzato in un turno, se seguo il percorso del codice X per ottenere qui il valore non sarà compreso tra 0 e 31, quindi il percorso del codice X è impossibile e non ho nemmeno bisogno di generare istruzioni per questo ". GCC lo fa famosomente per i controlli puntatori nulli. – MSalters

risposta

44

In C++ come in C, i turni sono limitati alla dimensione (in bit) del valore spostato. Ad esempio, se unsigned int è a 32 bit, uno spostamento di oltre 31 non è definito.

In pratica, un risultato comune è che vengono utilizzati i 5 bit meno significativi della quantità di spostamento ei bit di ordine superiore vengono ignorati; questo è dovuto al fatto che il compilatore produce un'istruzione macchina che fa esattamente questo (ad es. SHR su x86).

In questo caso, il valore di spostamento è 100000 (decimale) che risulta essere 11000011010100000 in binario - i 5 bit inferiori sono zero. Quindi, stai effettivamente ottenendo un cambiamento di 0. Non dovresti comunque fare affidamento su questo; tecnicamente, quello che stai vedendo è comportamento non definito.

Riferimenti:

Per C, N1570 sezione 6.5.7:

Se il valore dell'operando destro è negativo o è maggiore o uguale alla larghezza dell'operando sinistro promosso, il comportamento è indefinito.

Per C++, N3690 sezione 5.8 "[expr.shift]":

Il comportamento è indefinito se l'operando destro è negativo, o maggiore o uguale alla lunghezza in bit della promosso operando a sinistra.

N1570 è un progetto, quasi identico allo standard ISO C11 rilasciato; questa clausola è stata pressoché la stessa dallo standard ANSI C del 1989.

N3690 è una bozza recente dello standard C++; Non sono sicuro che sia il migliore da usare, ma ancora una volta, questa clausola non è cambiata.

+1

Hai osservato questo comportamento con diversi compilatori o leggi da alcuni dove? –

+3

@GrijeshChauhan, questo è documentato nelle specifiche standard C e C++. Per quanto riguarda la generazione dell'istruzione SHR, ho osservato questo. – davmac

+0

Holy mother of coding ... Ho appena provato questo e il binario di 100000 è '0b110000110101_00000'. Ho provato a passare da 100001, e in effetti è stato spostato di 1. – Suedocode

31

si invoca undefined behavior se si sposta maggiore della lunghezza in bit dell'operando di sinistra, la sezione draft C++ standard5.8operatori di spostamento paragrafo dice (sottolineatura mia):

Gli operandi devono essere di tipo di enumerazione integrale o senza ambito e vengono eseguite promozioni integrali.Il tipo del risultato è quello dell'operando sinistro promosso. Il comportamento non è definito se l'operando destro è negativo o maggiore o uguale alla lunghezza in bit dell'operando sinistro promosso.

interessante notare che sia gcc e clangpuò generare un avviso per questo codice, se la quantità di spostamento se un letterale:

cout << (b>> 100000) ; 

o se b è un const, l'avviso per gcc è il seguente:

warning: right shift count >= width of type [enabled by default] 

come sottolinea MSalters nei commenti alla domanda, potremmo non essere in grado di fare affidamento anche su questo avvertimento poiché questo è comportamento indefinito, che è coerente con gli standard nota sulla comportamento indefinito nei termini e Principali definizioni sezione che dice:

Nota: [...] comportamento indefinito ammissibile varia da ignorando la situazione completamente con risultati imprevedibili, a comportarsi durante la traduzione o l'esecuzione del programma in modo caratteristico documentato dell'ambiente (con o senza l'emissione di un messaggio diagnostico), per terminare una transla zione o esecuzione (con l'emissione di un messaggio diagnostico). [...]

piattaforma dettagli specifici

Una spiegazione potenziale per l'apparente mancanza di un cambiamento nel codice di esempio può essere perché su alcune piattaforme il valore di scorrimento sarà mascherato a 5 bits ad esempio su un x86 architettura possiamo vedere la sezione Intel® 64 and IA-32 Architectures Software Developer’s ManualSAL/SAR/SHL/SHR-shift in IA-32 Architettura Compatibilità sezione dice:

0.123.

L'8086 non maschera il numero di turni. Tuttavia, tutti gli altri processori IA-32 (a partire dal processore Intel 286) mascherano il numero di shift a 5 bit, risultando in un conteggio massimo di 31. [...]

+0

L'80286 ha consentito deliberatamente lo spostamento fino alla dimensione della parola, inclusa. Mi chiedo perché l'80386 non abbia fatto lo stesso (usando 6 bit di CL quando la dimensione dell'operando era di 32 bit)? – supercat

+0

Vedere anche [questo post del blog] (https://david.wragg.org/blog/2012/11/shift-instructions.html) per ulteriori informazioni su x86. –

Problemi correlati