2016-06-21 21 views
5

Ho fatto qualche test stupido sulla manipolazione dei bit e ho trovato questo problema. Eseguo questo codice:Bit di spostamento sinistro in c

int main(){ 
    unsigned int i; 
    for (i=1; i<34; i++) 
    { 
    unsigned long temp = i; 
    unsigned long mul = 1; 
    unsigned long val; 
    unsigned long one = 1; 

    // Way 1 
    while (temp--) 
     mul = mul << one; 

    // Way 2 
    val = (one<<i); 

    printf(" \n 1<<%i \n mul: 0x%X , val: 0x%X\n",i, mul, val); 
    } 
} 

Ovviamente, quando i> 31, verrà generato un overflow. Penso che entrambe le parti del codice (way1 e way2) dovrebbero produrre lo stesso risultato. Ma ottengo questo (alla fine):

/* ... correct results from i=1 to i=31 ... */ 
1<<30 
mul: 0x40000000 , val: 0x40000000 

1<<31 
mul: 0x80000000 , val: 0x80000000 

1<<32 
mul: **0x0** , val: **0x1** 

1<<33 
mul: **0x0** , val: **0x2** 

Perché, se entrambe le istruzioni sono lasciati turni, il programma produce risultati diversi? Sembra che la parte way2 produca un round shift, ma non so perché, penso davvero che "mul" abbia sempre il valore corretto.

compilo sotto una macchina 32bit Intel, versione di gcc 4.4.7

+5

Il risultato di un'operazione di spostamento con una quantità di spostamento maggiore della larghezza dell'operando non è definito per lo standard C. –

+0

Solo un parere, ma direi che in questi casi: 'mul << one' e' one << i', in realtà è meglio usare semplicemente '1' piuttosto che dargli il nome' one'. –

+0

Questo è il problema, sì. In questo caso, probabilmente è ANDs l'operando giusto con 0x3F prima di fare lo shft. –

risposta

2

In caso di

val = (one<<i); 

quando i ottiene maggiore o uguale a 32, il comportamento è indefinito.

 

Tuttavia, in caso di

while (temp--) 
    mul = mul << one; 

per spostamenti più di 32, si sposterà zero e il risultato è definito (zero).

+0

Sì. Penso che tu abbia ragione. Con la risposta di Aif penso sia chiaro. Molte grazie! – Alexi

9

Probabilmente perché questo è un comportamento indefinito? Secondo §6.5.7:

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

+0

Beh, potrei aspettarmi alcuni di entrambi i comportamenti, ma solo uno di essi in entrambi i casi. Non pensi che entrambe le operazioni siano uguali? EDIT: Vedo la risposta deepmax e ti capisco ora. Grazie! – Alexi

+0

@Alexi Capisci il significato di qualcosa che non è definito? –

+0

@EugeneSh. Sì, il mio problema era che comprendevo il significato di "comportamento indefinito", ma non sapevo perché c'erano due diferenti "comportamenti indefiniti" con la stessa (aparentemente) stessa operazione. – Alexi

-3

lamentele Quando compilo il codice con -Wall ho ottenuto:

BASH> gcc -Wall left-shift.c 
left-shift.c: In function ‘main’: 
left-shift.c:21:12: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 3 has type ‘long unsigned int’ [-Wformat=] 
    printf(" \n 1<<%i \n mul: 0x%X , val: 0x%X\n",i, mul, val); 
      ^
left-shift.c:21:12: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 4 has type ‘long unsigned int’ [-Wformat=] 

Così ho cambiato printf per

printf(" \n 1<<%i \n mul: 0x%lX , val: 0x%lX\n",i, mul, val); 

Con questo cambiamento "mul" e "val" mostra gli stessi risultati:

1<<30 
mul: 0x40000000 , val: 0x40000000 

1<<31 
mul: 0x80000000 , val: 0x80000000 

1<<32 
mul: 0x100000000 , val: 0x100000000 

1<<33 
mul: 0x200000000 , val: 0x200000000 

Informazioni di sistema:

BASH> gcc --version 
gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 
Copyright (C) 2015 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
BASH> uname -a 
Linux bm-pc-ubuntu 4.4.0-24-generiC#43-Ubuntu SMP Wed Jun 8 19:27:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 
BASH> lsb_release --all 
No LSB modules are available. 
Distributor ID: Ubuntu 
Description: Ubuntu 16.04 LTS 
Release: 16.04 
Codename: xenial 
+0

Questo perché la tua macchina è 64-bit e la sua è a 32-bit. – s7amuser

+0

Ignorando questo avvertimento, mi dà uscita anche strnager di quelli originali: 1 << 30 mul: 0x40000000, val: 0x40000000 1 << 31 mul: 0x80000000, val: 0x80000000 1 << 32 mul : 0x0, val: 0x0 1 << 33 mul: 0x0, val: 0x0 –

+0

Non si deve ignorare l'avviso. Se vuoi vedere risultati simili all'OP, prova ad aumentare il contatore del ciclo a 66. – s7amuser

0

Quando si esegue questa operazione:

val = (one<<i); 

Stai facendo un unico spostamento a sinistra per i. Se i è maggiore di 31, il risultato è undefined behavior, il che significa che il risultato potrebbe essere o meno quello che ti aspetti.

Dalla sezione 6.5.7.3 del C standard:

Le promozioni interi vengono eseguite su ciascuno degli operandi. Il tipo del risultato è quello dell'operando sinistro promosso.Se il valore dell'operando destro è negativo o è maggiore o uguale a uguale alla larghezza dell'operando di sinistra promosso, il comportamento è non definito.

Tuttavia, quando si esegue questa operazione:

while (temp--) 
    mul = mul << one; 

Stai facendo uno spostamento a sinistra di 1 i volte. Questo è ben definito, quindi ti dà il valore che ti aspetti.

Inoltre, si sta utilizzando %X per stampare un long quando si dovrebbe usare %lX. Ciò causa anche un comportamento non definito.