Il bit-shift dipende dall'endianità?


156

Supponiamo di avere il numero 'numb'=1025 [00000000 00000000 00000100 00000001]rappresentato:

Sulla macchina di Little Endian:

00000001 00000100 00000000 00000000

Sulla macchina Big Endian:

00000000 00000000 00000100 00000001

Ora, se applico il tasto Maiusc sinistro su 10 bit (cioè: intorpidito << = 10), dovrei avere:

[A] Sulla macchina di Little Endian:

Come ho notato in GDB, Little Endian esegue lo spostamento a sinistra in 3 passaggi: [Ho mostrato i passaggi '3' per comprendere meglio solo l'elaborazione]

  1. Tratta il no. nella Convenzione di Big Endian:

    00000000        00000000        00000100    00000001
  2. Applica Maiusc-Sinistra:

    00000000        00010000        00000100        00000000
  3. Rappresenta nuovamente il risultato in Little-Endian:

    00000000        00000100        00010000        00000000 

[B]. Sulla macchina Big Endian:

00000000        00010000        00000100        00000000

La mia domanda è:

Se applico direttamente uno spostamento a sinistra sulla Convenzione di Little Endian, dovrebbe dare:

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

Ma in realtà, dà:

00000000        00000100        00010000        00000000 

Per ottenere solo il secondo risultato, ho mostrato tre ipotetici passaggi sopra.

Spiegami perché i due risultati precedenti sono diversi: il risultato effettivo di numb << 10è diverso dal risultato previsto.

Risposte:


194

L'endianness è il modo in cui i valori vengono archiviati in memoria. Quando viene caricato nel processore, indipendentemente dall'endianness, l'istruzione bit shift funziona sul valore nel registro del processore. Pertanto, il caricamento dalla memoria al processore equivale alla conversione in big endian, l'operazione di spostamento viene successiva e quindi il nuovo valore viene memorizzato nuovamente in memoria, che è dove il piccolo ordine di byte endian ritorna in vigore.

Aggiorna, grazie a @jww: su PowerPC il vettore si sposta e ruota è sensibile all'endian. Puoi avere un valore in un registro vettoriale e uno spostamento produrrà risultati diversi su little-endian e big-endian .


4
Grazie per la spiegazione. Potete per favore suggerire qualche riferimento in cui posso ottenere una migliore comprensione di tali complessità.
Sandeep Singh,

4
La cosa migliore per comprendere l'endianità è usarla davvero su diverse architetture a livello incorporato. Tuttavia, potrei fare riferimento a questi due articoli: codeproject.com/KB/cpp/endianness.aspx e ibm.com/developerworks/aix/library/au-endianc/…
Carl

3
Quindi il mio codice funzionerà indipendentemente da Endian ?! è fantastico! Sono stato così preoccupato che avrei dovuto hackerare il mio codice all'inferno e ritorno!
MarcusJ,

2
@MarcusJ: non necessariamente. Ad esempio, se stai leggendo 4 byte da un file che rappresenta un numero intero a 32 bit, devi considerare l'endianità dei dati che stai leggendo insieme all'endianità del sistema che riceve i dati per interpretare correttamente i dati.
Carl

3
Su PowerPC il vettore si sposta e ruota è sensibile all'endian. Puoi avere un valore in un registro vettoriale e uno spostamento produrrà risultati diversi su little-endian e big-endian.
jww

58

No, bitshift, come qualsiasi altra parte di C, è definito in termini di valori , non di rappresentazioni. Lo spostamento a sinistra di 1 è la mutliplication di 2, lo spostamento a destra è la divisione. (Come sempre quando si utilizzano le operazioni bit a bit, fare attenzione alla firma. Tutto è meglio definito per i tipi integrali senza segno.)


1
Questo è fondamentalmente vero per l'aritmetica dei numeri interi, ma C fornisce molti casi di comportamento dipendente dalla rappresentazione.
Edmund,

2
@Edmund: Hm ... in particolare non è specificata l'implementazione della signness, e di conseguenza il comportamento delle operazioni bit a bit (come il tasto destro) e del modulo e della divisione sono implementazioni definite su numeri interi negativi. Quali altre cose hai in mente che sono definite dall'implementazione?
Kerrek SB,

@KerrekSB sfortunatamente non sono implementazioni definite su numeri interi negativi. Non sono specificati in C89 e indefiniti in C99 +, che è stata una pessima idea.
Paolo Bonzini,

@PaoloBonzini: Sì, buon punto. In realtà è ancora meglio, poiché rafforza il punto in cui le operazioni di spostamento sono definite in termini di valori, probabilmente non definite quando il risultato non è rappresentabile e che le speculazioni sulla rappresentazione sottostante non aiutano.
Kerrek SB,

@KerrekSB: il fatto è che tutti hanno effettivamente bisogno del turno di sinistra per essere rappresentati sia come valori che come rappresentazione, a seconda del caso. L'uso di numeri interi senza segno può causare altri problemi, ad esempio x &= -1u << 20molto probabilmente non sarà corretto se xè a 64 bit e a int32 bit. Per questo motivo, GCC promette di non trattare mai i turni firmati come indefiniti o addirittura non specificati.
Paolo Bonzini,

5

Qualunque istruzione di spostamento sposta prima i bit di ordine superiore, viene considerato lo spostamento di sinistra. Qualunque istruzione di spostamento sposta prima i bit di ordine inferiore, viene considerato lo spostamento giusto. In tal senso, il comportamento di >>e <<per i unsignednumeri non dipenderà dall'endianità.


4

I computer non scrivono i numeri come facciamo noi. Il valore cambia semplicemente. Se insisti a guardarlo byte per byte (anche se non è così che lo fa il computer), potresti dire che su una macchina little-endian, il primo byte si sposta a sinistra, i bit in eccesso vanno nel secondo byte, e così via.

(A proposito, little-endian ha più senso se si scrivono i byte in verticale piuttosto che in orizzontale, con indirizzi più alti in cima. Che è il modo in cui i diagrammi delle mappe di memoria sono comunemente disegnati.)


2

Sebbene la risposta accettata sottolinei che l'endianità è un concetto dal punto di vista della memoria. Ma non credo che risponda direttamente alla domanda.

Alcune risposte mi dicono che le operazioni bit a bit non dipendono da endianess e che il processore può rappresentare i byte in qualsiasi altro modo. Ad ogni modo, si sta parlando che l'endianess viene sottratta.

Ma quando facciamo alcuni calcoli bit per bit sul documento, ad esempio, non è necessario indicare l'endianità in primo luogo? La maggior parte delle volte scegliamo implicitamente una endianess.

Ad esempio, supponiamo di avere una riga di codice come questa

0x1F & 0xEF

Come calcoleresti il ​​risultato a mano, su un foglio?

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

Quindi qui usiamo un formato Big Endian per fare il calcolo. Puoi anche usare Little Endian per calcolare e ottenere lo stesso risultato.

A proposito, quando scriviamo numeri in codice, penso che sia come un formato Big Endian. 123456oppure 0x1F, i numeri più significativi iniziano da sinistra.

Ancora una volta, non appena scriviamo un formato binario di un valore sulla carta, penso che abbiamo già scelto un Endianess e stiamo vedendo il valore come lo vediamo dalla memoria.

Quindi, tornando alla domanda, un'operazione di spostamento <<dovrebbe essere considerata come spostamento da LSB (byte meno significativo) a MSB (byte più significativo) .

Quindi, come per l'esempio nella domanda:

numb=1025

Little Endian

LSB 00000001 00000100 00000000 00000000 MSB

Quindi << 10passerebbe 10bitda LSB a MSB.


Confronto e << 10operazioni per il formato Little Endian passo dopo passo:

MSB                                        LSB
    00000000  00000000  00000100  00000001  numb(1025)
    00000000  00010000  00000100  00000000  << 10

LSB                                        MSB
    00000000  00000100  00010000  00000000 numb(1025) << 10, and put in a Little Endian Format

LSB                                        MSB
    00000001  00000100  00000000  00000000 numb(1205) in Little Endian format
    00000010  00001000  00000000  00000000 << 1 
    00000100  00010000  00000000  00000000 << 2 
    00001000  00100000  00000000  00000000 << 3 
    00010000  01000000  00000000  00000000 << 4
    00100000  10000000  00000000  00000000 << 5
    01000000  00000000  00000001  00000000 << 6
    10000000  00000000  00000010  00000000 << 7
    00000000  00000001  00000100  00000000 << 8
    00000000  00000010  00001000  00000000 << 9
    00000000  00000100  00010000  00000000 << 10 (check this final result!)

Wow! Ottengo il risultato atteso come descritto dall'OP!

I problemi che l'OP non ha ottenuto il risultato atteso sono i seguenti:

  1. Sembra che non sia passato da LSB a MSB.

  2. Quando si spostano i bit nel formato Little Endian, dovresti capire (grazie a Dio me ne rendo conto) che:

LSB 10000000 00000000 MSB << 1è
LSB 00000000 00000001 MSB, non è LSB 01000000 00000000 MSB

Perché per ogni individuo 8bits, in realtà lo stiamo scrivendo in un MSB 00000000 LSBformato Big Endian.

Quindi è come

LSB[ (MSB 10000000 LSB) (MSB 00000000 LSB) ]MSB


Per riassumere:

  1. Sebbene si dice che le operazioni bit per bit siano astratte via blablablabla ..., quando calcoliamo manualmente le operazioni bit per bit, dobbiamo ancora sapere quale endianità stiamo usando mentre scriviamo il formato binario sul foglio. Inoltre, dobbiamo assicurarci che tutti gli operatori utilizzino la stessa endianess.

  2. L'OP non ha ottenuto il risultato atteso perché ha sbagliato il cambio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.