2009-12-07 16 views
22

Ho codice C in cui eseguire le seguenti operazioni.Numeri negativi con spostamento a destra in C

int nPosVal = +0xFFFF; // + Added for ease of understanding 
int nNegVal = -0xFFFF; // - Added for valid reason 

Ora in cui provo

printf ("%d %d", nPosVal >> 1, nNegVal >> 1); 

ottengo

32767 -32768 

È questo prevede?

io sono in grado di pensare una cosa del genere

65535 >> 1 = (int) 32767.5 = 32767 
-65535 >> 1 = (int) -32767.5 = -32768 

Cioè, -32767,5 arrotondato a -32768.

Questa comprensione è corretta?

risposta

32

sembra che la tua implementazione è probabilmente facendo uno spostamento aritmetico bit con due numeri di complemento. In questo sistema, sposta tutti i bit a destra e quindi riempie i bit superiori con una copia di qualunque fosse l'ultimo bit. Così, per il tuo esempio, int trattandoli come 32 bit qui:

nPosVal = 00000000000000001111111111111111 
nNegVal = 11111111111111110000000000000001 

Dopo il turno, hai:

nPosVal = 00000000000000000111111111111111 
nNegVal = 11111111111111111000000000000000 

Se si converte questo torna a decimale, si ottiene 32767 e -32768 rispettivamente.

In effetti, uno spostamento a destra arrotonda verso l'infinito negativo.

Edit: Secondo la sezione 6.5.7 della più recente draft standard, questo comportamento su numeri negativi dipende dall'implementazione:

Il risultato di E1 >> E2 è E1 E2 posizioni di bit a destra in differita. Se E1 ha un tipo senza segno o se E1 ha un tipo firmato e un valore non negativo, il valore del risultato è la parte integrante del quoziente di E1/2 E2. Se E1 ha un tipo firmato e un valore negativo, il valore risultante è definito dall'implementazione.

loro dichiarato rational per questo:

Il Comitato C89 ha affermato la libertà nella realizzazione concesso da K & R che non richiedono l'operazione di spostamento a destra firmato a firmare estendere, dal momento che un tale obbligo potrebbe rallentare codice veloce e dal momento che l'utilità segno di cambiamenti prolungati è marginale. (Lo spostamento di un negativo complemento a due intero aritmeticamente giusto posto è non lo stesso dividendo per due!)

Quindi è a carico di attuazione in teoria. In pratica, non ho mai visto un'implementazione non fare uno spostamento aritmetico proprio quando è firmato l'operando di sinistra.

+0

+1, questo è quello che volevo sapere. Il giusto turno arrotonda all'infinito negativo. Ma è documentato? – Alphaneo

+0

Dipende dall'implementazione. (Vedi la mia modifica sopra.) Come ho già detto, non ho mai visto un'implementazione diversa su questo, ma teoricamente potrebbe. – Boojum

2

Quando si sposta a destra, il bit meno significativo viene scartato.

0xFFFF = 0 1111 1111 1111 1111, che giuste turni per dare 0 0111 1111 1111 1111 = 0x7FFF

-0xFFFF = 1 0000 0000 0000 0001 (complemento di 2), che giuste turni di 1 1000 0000 0000 0000 = -0x8000

7

La specifica C non specifica se il bit di segno è spostato o meno. È dipendente dall'implementazione.

+0

Non credo che sia il suo problema ... – rlbond

+0

Q-1 ha chiesto se il risultato era previsto. La mia risposta suggerisce che no, non puoi aspettarti alcun dato per il giusto spostamento del numero negativo senza prima consultare la documentazione del compilatore – Trent

+0

Puoi darmi un link, dove lo standard lo dice? – hirschhornsalz

3

A-1: ​​Sì. 0xffff >> 1 è 0x7fff o 32767. Non sono sicuro di cosa fa -0xffff. È strano

A-2: lo spostamento non è la stessa cosa della divisione. È un bit shifting, un'operazione binaria primitiva. Che a volte può essere utilizzato per alcuni tipi di divisione è conveniente, ma non sempre lo stesso.

+0

I letterali interi sono firmati, quindi '- 0xFFFF' nega' 0xFFFF'. Cioè, è uguale a '(~ 0xFFFF) -1', interpretato come un intero con segno. – outis

18

No, non si ottengono numeri frazionari come 0.5 quando si lavora con numeri interi. I risultati possono essere facilmente spiegate quando si guardano le rappresentazioni binarie dei due numeri:

 65535: 00000000000000001111111111111111 
    -65535: 11111111111111110000000000000001 

Bit spostamento a destra di un bit, e si estendono a sinistra (si noti che questo è carico di attuazione, grazie Trento):

65535 >> 1: 00000000000000000111111111111111 
-65535 >> 1: 11111111111111111000000000000000 

riconvertire decimali:

65535 >> 1 = 32767 
-65535 >> 1 = -32768 
+6

Si noti che l'estensione a sinistra dipende dall'implementazione. – Trent

+2

Lo standard dice: "Se il valore dell'operando di destra è negativo o è maggiore o uguale alla larghezza dell'operando di sinistra promosso, il comportamento non è definito." – Gonzalo

+0

@Trent: sei sicuro? Pensavo che l'estensione del segno dipendesse dalla firma dell'operando di sinistra. – hirschhornsalz

2

Sotto il livello C, le macchine hanno un core CPU che è interamente intero o scalare. Sebbene in questi giorni ogni CPU desktop abbia una FPU, questo non è sempre stato il caso e anche oggi i sistemi embedded sono realizzati senza istruzioni in virgola mobile.

I paradigmi di programmazione di oggi, i design e le lingue della CPU risalgono all'epoca in cui la FPU potrebbe non esistere nemmeno.

Quindi, le istruzioni CPU implementano operazioni a virgola fissa, generalmente trattati come puramente intero op. Solo se un programma dichiara articoli di float o doppio esistono frazioni. (Bene, puoi usare le CPU ops per "punto fisso" con frazioni ma questo è ora e sempre abbastanza raro.)

Indipendentemente da quanto richiesto da un comitato standard di linguaggio anni fa, tutte le macchine ragionevoli propagano il segno un po 'a destra di numeri firmati. I cambiamenti a destra dei valori senza segno si spostano sugli zeri a sinistra. I pezzi spostati a destra sono lasciati cadere sul pavimento.

Per maggiore comprensione è necessario esaminare "aritmetica a complemento doppio".

+0

Penso che la tua definizione di "macchina ragionevole" sia ristretta. – Trent

+0

Se la mia definizione è ristretta, per favore denominate una singola macchina per cui 'x >> 1', in C, trasformerà un numero negativo in uno positivo. – DigitalRoss

+0

Il compilatore Microchip C18 (un collegamento alla guida per l'utente può essere trovato qui: http://tinyurl.com/ybt2svs - vedere la sezione B.4) – Trent

Problemi correlati