Operazione e utilizzo bit per bit


102

Considera questo codice:

x = 1        # 0001
x << 2       # Shift left 2 bits: 0100
# Result: 4

x | 2        # Bitwise OR: 0011
# Result: 3

x & 1        # Bitwise AND: 0001
# Result: 1

Riesco a capire gli operatori aritmetici in Python (e in altri linguaggi), ma non ho mai capito abbastanza bene gli operatori "bit per bit". Nell'esempio sopra (da un libro Python), capisco lo spostamento a sinistra ma non gli altri due.

Inoltre, a cosa servono effettivamente gli operatori bit per bit? Apprezzerei alcuni esempi.



Risposte:


163

Gli operatori bit per bit sono operatori che lavorano su valori multi-bit, ma concettualmente un bit alla volta.

  • ANDè 1 solo se entrambi i suoi ingressi sono 1, altrimenti è 0.
  • ORè 1 se uno o entrambi i suoi ingressi sono 1, altrimenti è 0.
  • XORè 1 solo se esattamente uno dei suoi ingressi è 1, altrimenti è 0.
  • NOT è 1 solo se il suo input è 0, altrimenti è 0.

Questi possono spesso essere mostrati al meglio come tabelle di verità. Le possibilità di input sono in alto e a sinistra, il bit risultante è uno dei quattro (due nel caso di NOT poiché ha un solo input) valori mostrati all'intersezione degli input.

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

Un esempio è se vuoi solo i 4 bit inferiori di un intero, lo AND con 15 (binario 1111) quindi:

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

I bit zero in 15 in quel caso agiscono effettivamente come un filtro, costringendo anche i bit nel risultato a essere zero.

Inoltre, >>e <<sono spesso inclusi come operatori bit per bit, e "spostano" un valore rispettivamente a destra ea sinistra di un certo numero di bit, gettando via i bit che rotolano dall'estremità verso cui ti stai spostando e alimentando zero bit al altra fine.

Quindi, ad esempio:

1001 0101 >> 2 gives 0010 0101
1111 1111 << 4 gives 1111 0000

Si noti che lo spostamento a sinistra in Python è insolito in quanto non utilizza una larghezza fissa in cui i bit vengono scartati: mentre molti linguaggi utilizzano una larghezza fissa in base al tipo di dati, Python espande semplicemente la larghezza per soddisfare i bit extra. Per ottenere il comportamento di scarto in Python, puoi seguire uno spostamento a sinistra con un bit andper bit come in un valore a 8 bit spostando a sinistra di quattro bit:

bits8 = (bits8 << 4) & 255

Con questo in mente, un altro esempio di operatori bit a bit è se si dispone di due valori di 4 bit che si desidera mettere in valigia in un 8-bit uno, è possibile utilizzare tutti e tre i operatori ( left-shift, ande or):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • L' & 15operazione farà in modo che entrambi i valori abbiano solo i 4 bit inferiori.
  • Il << 4è uno spostamento a 4 bit sinistra per spostare val1nella top 4 bit di un valore a 8 bit.
  • Il |combina semplicemente questi due insieme.

Se val1è 7 e val2è 4:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100

43

Un utilizzo tipico:

| viene utilizzato per impostare un determinato bit a 1

& viene utilizzato per testare o cancellare un determinato bit

  • Imposta un bit (dove n è il numero di bit e 0 è il bit meno significativo):

    unsigned char a |= (1 << n);

  • Cancella un po ':

    unsigned char b &= ~(1 << n);

  • Cambia un po ':

    unsigned char c ^= (1 << n);

  • Prova un po ':

    unsigned char e = d & (1 << n);

Prendi il caso della tua lista per esempio:

x | 2viene utilizzato per impostare il bit 1 di xa 1

x & 1viene utilizzato per verificare se il bit 0 di xè 1 o 0


38

a cosa servono effettivamente gli operatori bit per bit? Apprezzerei alcuni esempi.

Uno degli usi più comuni delle operazioni bit per bit è l'analisi dei colori esadecimali.

Ad esempio, ecco una funzione Python che accetta una stringa simile #FF09BEe restituisce una tupla dei suoi valori Red, Green e Blue.

def hexToRgb(value):
    # Convert string to hexadecimal number (base 16)
    num = (int(value.lstrip("#"), 16))

    # Shift 16 bits to the right, and then binary AND to obtain 8 bits representing red
    r = ((num >> 16) & 0xFF)

    # Shift 8 bits to the right, and then binary AND to obtain 8 bits representing green
    g = ((num >> 8) & 0xFF)

    # Simply binary AND to obtain 8 bits representing blue
    b = (num & 0xFF)
    return (r, g, b)

So che ci sono modi più efficienti per ottenere questo risultato, ma credo che questo sia un esempio davvero conciso che illustra sia i turni che le operazioni booleane bit per bit.


14

Penso che la seconda parte della domanda:

Inoltre, a cosa servono effettivamente gli operatori bit per bit? Apprezzerei alcuni esempi.

È stato affrontato solo parzialmente. Questi sono i miei due centesimi su quella materia.

Le operazioni bit per bit nei linguaggi di programmazione giocano un ruolo fondamentale quando si tratta di molte applicazioni. Quasi tutto il calcolo di basso livello deve essere eseguito utilizzando questo tipo di operazioni.

In tutte le applicazioni che devono inviare dati tra due nodi, come ad esempio:

  • reti di computer;

  • applicazioni di telecomunicazione (telefoni cellulari, comunicazioni satellitari, ecc.).

Nel livello di comunicazione di livello inferiore, i dati vengono solitamente inviati in quelli che vengono chiamati frame . I frame sono solo stringhe di byte che vengono inviate attraverso un canale fisico. Questi frame di solito contengono i dati effettivi più alcuni altri campi (codificati in byte) che fanno parte di ciò che viene chiamato intestazione . L'intestazione di solito contiene byte che codificano alcune informazioni relative allo stato della comunicazione (ad esempio, con flag (bit)), contatori di frame, codici di correzione e rilevamento degli errori, ecc. Per ottenere i dati trasmessi in un frame e per costruire il frame per inviare dati, avrai bisogno di certe operazioni bit per bit.

In generale, quando si ha a che fare con quel tipo di applicazioni, è disponibile un'API in modo da non dover gestire tutti quei dettagli. Ad esempio, tutti i linguaggi di programmazione moderni forniscono librerie per le connessioni socket, quindi non è effettivamente necessario creare i frame di comunicazione TCP / IP. Ma pensa alle brave persone che hanno programmato quelle API per te, hanno dovuto sicuramente occuparsi della costruzione del telaio; utilizzando tutti i tipi di operazioni bit per bit per andare avanti e indietro dalla comunicazione di basso livello a quella di livello superiore.

Come esempio concreto, immagina che qualcuno ti dia un file che contiene dati grezzi che sono stati catturati direttamente dall'hardware di telecomunicazione. In questo caso, per trovare i frame, sarà necessario leggere i byte grezzi nel file e provare a trovare un qualche tipo di parole di sincronizzazione, scansionando i dati bit per bit. Dopo aver identificato le parole di sincronizzazione, sarà necessario per ottenere i fotogrammi effettivi, e SHIFT loro se necessario (e che è solo l'inizio della storia) per ottenere i dati effettivi che viene trasmesso.

Un'altra famiglia di applicazioni di basso livello molto diversa è quando è necessario controllare l'hardware utilizzando alcune porte (un po 'antiche), come porte parallele e seriali. Queste porte sono controllate impostando alcuni byte, e ogni bit di quei byte ha un significato specifico, in termini di istruzioni, per quella porta (vedere ad esempio http://en.wikipedia.org/wiki/Parallel_port ). Se vuoi creare software che faccia qualcosa con quell'hardware avrai bisogno di operazioni bit per bit per tradurre le istruzioni che vuoi eseguire nei byte che la porta comprende.

Ad esempio, se hai alcuni pulsanti fisici collegati alla porta parallela per controllare qualche altro dispositivo, questa è una riga di codice che puoi trovare nell'applicazione software:

read = ((read ^ 0x80) >> 4) & 0x0f; 

Spero che questo contribuisca.


Aggiungerei en.wikipedia.org/wiki/Bit_banging come un'altra strada da esplorare, soprattutto se si leggono le porte parallele e seriali come esempio in cui le operazioni bit per bit possono essere utili.
Dan il

6

Spero che questo chiarisca quei due:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

4
Oops ... ho cercato di essere la pistola più veloce dell'ovest ... è finito come un idiota che non conosce nemmeno il binario per due :( Risolto.
Amarghosh

1
x & 1non illustra l'effetto come x & 2sarebbe.
dansalmo

5

Pensa a 0 come falso e 1 come vero. Quindi bit per bit e (&) e o (|) funzionano proprio come normali e e o tranne che fanno tutti i bit del valore contemporaneamente. Tipicamente li vedrai usati per i flag se hai 30 opzioni che possono essere impostate (ad esempio come stili di disegno su una finestra) non vuoi dover passare 30 valori booleani separati per impostare o disimpostare ciascuno, quindi usi | per combinare le opzioni in un unico valore e quindi utilizzare & per verificare se ciascuna opzione è impostata. Questo stile di passaggio delle bandiere è ampiamente utilizzato da OpenGL. Poiché ogni bit è un flag separato, ottieni valori di flag su potenze di due (ovvero numeri che hanno un solo bit impostato) 1 (2 ^ 0) 2 (2 ^ 1) 4 (2 ^ 2) 8 (2 ^ 3) il la potenza di due ti dice quale bit è impostato se il flag è attivo.

Nota anche 2 = 10 quindi x | 2 è 110 (6) non 111 (7) Se nessuno dei bit si sovrappone (il che è vero in questo caso) | agisce come addizione.


5

Non l'ho visto menzionato sopra, ma vedrai anche alcune persone che usano lo shift sinistro e destro per le operazioni aritmetiche. Uno spostamento a sinistra per x equivale a moltiplicare per 2 ^ x (purché non trabocchi) e uno spostamento a destra equivale a dividere per 2 ^ x.

Recentemente ho visto persone usare x << 1 ex >> 1 per raddoppiare e dimezzare, anche se non sono sicuro che stiano solo cercando di essere intelligenti o se ci sia davvero un netto vantaggio rispetto ai normali operatori.


1
Non so nulla di python, ma in linguaggi di livello inferiore come C o anche inferiore - assembly, lo spostamento bit a bit è molto più efficiente. Per vedere la differenza, puoi scrivere un programma in C facendo questo in ogni modo e compilare semplicemente in codice assembly (o se conosci assembly lang, lo sapresti già :)). Vedere la differenza nel numero di istruzioni.
0xc0de

2
La mia argomentazione contro l'uso degli operatori di spostamento di bit sarebbe che la maggior parte dei compilatori moderni probabilmente sta già ottimizzando le operazioni aritmetiche, quindi l'intelligenza è nel migliore dei casi discutibile o nel peggiore dei casi combatte il compilatore. Non ho esperienza in C, compilatori o progetti di CPU e quindi non presumo di avere ragione. :)
P. Stallworth

Questo dovrebbe essere più alto. Ho avuto a che fare con un codice che utilizzava l'operatore bit a bit esattamente in quel modo e quella risposta mi ha aiutato a capire le cose.
Philippe Oger

4

Imposta

Gli insiemi possono essere combinati utilizzando operazioni matematiche.

  • L'operatore union |combina due set per formarne uno nuovo contenente elementi in entrambi.
  • L'operatore di intersezione &ottiene gli elementi solo in entrambi.
  • L'operatore di differenza -ottiene gli elementi nel primo set ma non nel secondo.
  • L'operatore differenza simmetrica ^ottiene gli elementi in uno dei due set, ma non in entrambi.

Provate voi stessi:

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)

print(first & second)

print(first - second)

print(second - first)

print(first ^ second)

Risultato:

{1, 2, 3, 4, 5, 6, 7, 8, 9}

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

{1, 2, 3, 7, 8, 9}

Questa risposta è completamente estranea alla domanda e sembra essere stata copiata e incollata altrove.
doctaphred

La domanda chiede "Per cosa sono effettivamente utilizzati gli operatori bit per bit?". Questa risposta fornisce un utilizzo meno noto ma molto utile degli operatori bit per bit.
Taegyung

3

Questo esempio ti mostrerà le operazioni per tutti e quattro i valori a 2 bit:

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

Ecco un esempio di utilizzo:

x = raw_input('Enter a number:')
print 'x is %s.' % ('even', 'odd')[x&1]

2

Un altro caso d'uso comune è la manipolazione / verifica delle autorizzazioni dei file. Vedi il modulo stat di Python: http://docs.python.org/library/stat.html .

Ad esempio, per confrontare le autorizzazioni di un file con un set di autorizzazioni desiderato, potresti fare qualcosa come:

import os
import stat

#Get the actual mode of a file
mode = os.stat('file.txt').st_mode

#File should be a regular file, readable and writable by its owner
#Each permission value has a single 'on' bit.  Use bitwise or to combine 
#them.
desired_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR

#check for exact match:
mode == desired_mode
#check for at least one bit matching:
bool(mode & desired_mode)
#check for at least one bit 'on' in one, and not in the other:
bool(mode ^ desired_mode)
#check that all bits from desired_mode are set in mode, but I don't care about 
# other bits.
not bool((mode^desired_mode)&desired_mode)

Ho espresso i risultati come booleani, perché mi interessa solo la verità o la falsità, ma sarebbe un esercizio utile stampare i valori bin () per ciascuno di essi.


1
Ti sbagli nell'ultimo esempio. Ecco come dovrebbe assomiglia: not bool((mode ^ desired_mode) & 0777). Oppure (più facile da capire): not (mode & 0777) ^ desired_mode == 0. E lascerà solo bit interessanti, XOR controllerà quali sono tutti i bit desiderati impostati. Il == 0confronto esplicito è più significativo di bool().
Vadim Fint

Non penso che questo sia specifico per le operazioni sui file. Ad esempio, in PyQt fai qualcosa di simile per setWindowFlags. Esempio: setWindowFlags(SplashScreen | WindowStaysOnTopHint). Lo trovo ancora confuso, in quanto sembra un interruttore che stai impostando su "on", quindi in questo caso sembra più intuitivo "e".
eric

2

Le rappresentazioni in bit di interi vengono spesso utilizzate nel calcolo scientifico per rappresentare matrici di informazioni vero-falso perché un'operazione bit per bit è molto più veloce dell'iterazione attraverso una matrice di valori booleani. (I linguaggi di livello superiore possono utilizzare l'idea di un array di bit.)

Un esempio carino e abbastanza semplice di questo è la soluzione generale al gioco di Nim. Dai un'occhiata al codice Python sulla pagina Wikipedia . Si fa un uso pesante di esclusiva OR bit a bit, ^.


1

Potrebbe esserci un modo migliore per trovare dove si trova un elemento dell'array tra due valori, ma come mostra questo esempio, il & funziona qui, mentre e non lo fa.

import numpy as np
a=np.array([1.2, 2.3, 3.4])
np.where((a>2) and (a<3))      
#Result: Value Error
np.where((a>2) & (a<3))
#Result: (array([1]),)

1

non l'ho visto menzionato, questo esempio ti mostrerà l'operazione decimale (-) per valori a 2 bit: AB (solo se A contiene B)

questa operazione è necessaria quando nel nostro programma teniamo un verbo che rappresenta dei bit. a volte dobbiamo aggiungere bit (come sopra) e talvolta dobbiamo rimuovere bit (se il verbo contiene quindi)

111 #decimal 7
-
100 #decimal 4
--------------
011 #decimal 3

con python: 7 & ~ 4 = 3 (rimuovi da 7 i bit che rappresentano 4)

001 #decimal 1
-
100 #decimal 4
--------------
001 #decimal 1

con python: 1 & ~ 4 = 1 (rimuovi da 1 i bit che rappresentano 4 - in questo caso 1 non è 'contiene' 4) ..


0

Sebbene la manipolazione dei bit di un numero intero sia utile, spesso per i protocolli di rete, che possono essere specificati fino al bit, si può richiedere la manipolazione di sequenze di byte più lunghe (che non sono facilmente convertite in un numero intero). In questo caso è utile utilizzare la libreria bitstring che consente operazioni bit per bit sui dati - ad esempio si può importare la stringa 'ABCDEFGHIJKLMNOPQ' come stringa o come hex e bit shift (o eseguire altre operazioni bit per bit):

>>> import bitstring
>>> bitstring.BitArray(bytes='ABCDEFGHIJKLMNOPQ') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
>>> bitstring.BitArray(hex='0x4142434445464748494a4b4c4d4e4f5051') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')

0

i seguenti operatori bit per bit: & , | , ^ e ~ restituiscono valori (in base al loro input) nello stesso modo in cui le porte logiche influenzano i segnali. Potresti usarli per emulare i circuiti.


0

Per capovolgere i bit (cioè il complemento / inversione di 1) puoi fare quanto segue:

Poiché il valore ExORed con tutti gli 1 risulta in inversione, per una data larghezza di bit è possibile utilizzare ExOR per invertirli.

In Binary
a=1010 --> this is 0xA or decimal 10
then 
c = 1111 ^ a = 0101 --> this is 0xF or decimal 15
-----------------
In Python
a=10
b=15
c = a ^ b --> 0101
print(bin(c)) # gives '0b101'
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.