Quali sono alcuni casi d'uso reali dei seguenti operatori bit per bit?
- E
- XOR
- NON
- O
- Spostamento sinistro / destro
Quali sono alcuni casi d'uso reali dei seguenti operatori bit per bit?
Risposte:
Campi bit (flag)
Sono il modo più efficiente di rappresentare qualcosa il cui stato è definito da diverse proprietà "sì o no". Le ACL sono un buon esempio; se hai, diciamo, 4 permessi discreti (leggi, scrivi, esegui, modifica politica), è meglio archiviarlo in 1 byte piuttosto che sprecare 4. Questi possono essere associati ai tipi di enumerazione in molte lingue per una maggiore comodità.
La comunicazione su porte / socket
implica sempre checksum, parità, bit di stop, algoritmi di controllo del flusso e così via, che di solito dipendono dai valori logici dei singoli byte rispetto ai valori numerici, poiché il supporto potrebbe essere in grado di trasmettere solo un bit a un tempo.
Compressione, crittografia
Entrambi dipendono fortemente dagli algoritmi bit a bit. Guarda l' algoritmo deflate per un esempio: tutto è in bit, non in byte.
Macchine a stati finiti
Sto parlando principalmente del tipo incorporato in alcuni componenti hardware, sebbene possano essere trovati anche nel software. Questi sono combinatoria in natura - si potrebbe letteralmente essere sempre "compilato" verso il basso per un gruppo di porte logiche, quindi devono essere espresso come AND
, OR
, NOT
, etc.
Grafica
Non c'è abbastanza spazio qui per entrare in ogni area in cui questi operatori vengono utilizzati nella programmazione grafica. XOR
(o ^
) è particolarmente interessante qui perché l'applicazione dello stesso input una seconda volta annullerà la prima. Le vecchie GUI si basavano su questo per evidenziare la selezione e altri overlay, al fine di eliminare la necessità di costosi ridisegni. Sono ancora utili nei protocolli di grafica lenta (cioè desktop remoto).
Questi sono stati solo i primi esempi che mi sono venuti in mente - questo non è certo un elenco esaustivo.
È strano?
(value & 0x1) > 0
È divisibile per due (pari)?
(value & 0x1) == 0
Ecco alcuni modi di dire comuni che si occupano di flag memorizzati come singoli bit.
enum CDRIndicators {
Local = 1 << 0,
External = 1 << 1,
CallerIDMissing = 1 << 2,
Chargeable = 1 << 3
};
unsigned int flags = 0;
Imposta la bandiera addebitabile:
flags |= Chargeable;
Cancella ID chiamanteMissing flag:
flags &= ~CallerIDMissing;
Verifica se sono impostati CallerIDMissing e Chareable:
if((flags & (CallerIDMissing | Chargeable )) == (CallerIDMissing | Chargeable)) {
}
Ho usato operazioni bit per bit nell'implementazione di un modello di sicurezza per un CMS. Aveva pagine a cui gli utenti potevano accedere se fossero in gruppi appropriati. Un utente potrebbe essere in più gruppi, quindi è necessario verificare se esiste un'intersezione tra i gruppi di utenti e i gruppi di pagine. Quindi abbiamo assegnato a ciascun gruppo un identificativo univoco di potenza di 2, ad esempio:
Group A = 1 --> 00000001
Group B = 2 --> 00000010
Group C = 3 --> 00000100
OR di questi valori insieme e memorizziamo il valore (come un singolo int) con la pagina. Ad esempio, se ai gruppi A e B è possibile accedere a una pagina, memorizziamo il valore 3 (che in binario è 00000011) come controllo dell'accesso alle pagine. Allo stesso modo, memorizziamo un valore di identificatori di gruppo ORed con un utente per rappresentare in quali gruppi si trovano.
Quindi, per verificare se un determinato utente può accedere a una determinata pagina, devi solo ANDARE insieme i valori e verificare se il valore è diverso da zero. Questo è molto veloce poiché questo controllo è implementato in una singola istruzione, nessun loop, nessun round trip di database.
La programmazione di basso livello è un buon esempio. Ad esempio, potrebbe essere necessario scrivere un bit specifico in un registro mappato in memoria per fare in modo che un componente hardware faccia ciò che si desidera:
volatile uint32_t *register = (volatile uint32_t *)0x87000000;
uint32_t value;
uint32_t set_bit = 0x00010000;
uint32_t clear_bit = 0x00001000;
value = *register; // get current value from the register
value = value & ~clear_bit; // clear a bit
value = value | set_bit; // set a bit
*register = value; // write it back to the register
Inoltre, htonl()
e htons()
sono implementati usando gli operatori &
e |
(su macchine il cui endianness (ordine di byte) non corrisponde all'ordine di rete):
#define htons(a) ((((a) & 0xff00) >> 8) | \
(((a) & 0x00ff) << 8))
#define htonl(a) ((((a) & 0xff000000) >> 24) | \
(((a) & 0x00ff0000) >> 8) | \
(((a) & 0x0000ff00) << 8) | \
(((a) & 0x000000ff) << 24))
htons()
e htonl()
sono funzioni POSIX per scambiare a short
o a dall'endianness long
host ( h
) all'ordine n
byte di rete ( ).
htonl()
per un int
valore a 32 bit ? long
significa 64 bit in molte lingue.
Li uso per ottenere valori RGB (A) da valori cromatici compressi, ad esempio.
(a & b) >> c
è più di 5 volte più veloce di a % d / e
(entrambi i modi per estrarre un singolo valore di colore da un int che rappresenta ARGB). Rispettivamente, 6,7 e 35,2 per 1 miliardo di iterazioni.
%
non è l'operatore Modulus, è l'operatore Remainder. Sono equivalenti per valori positivi ma differiscono da quelli negativi. Se si forniscono le restrizioni appropriate (passando un uint
anziché int
ad esempio), i due esempi dovrebbero avere la stessa velocità.
Quando ho un sacco di bandiere booleane, mi piace memorizzarle tutte in un int.
Li tiro fuori usando bitwise-AND. Per esempio:
int flags;
if (flags & 0x10) {
// Turn this feature on.
}
if (flags & 0x08) {
// Turn a second feature on.
}
eccetera.
if (flags.feature_one_is_one) { // turn on feature }
. È nello standard ANSI C, quindi la portabilità non dovrebbe essere un problema.
& = AND:
maschera bit specifici.
Stai definendo i bit specifici che dovrebbero essere visualizzati o non visualizzati. 0x0 e x cancelleranno tutti i bit in un byte mentre 0xFF non cambierà x. 0x0F visualizzerà i bit nel bocconcino inferiore.
Conversione:
per eseguire il cast di variabili più brevi in più lunghe con identità bit è necessario regolare i bit poiché -1 in un int è 0xFFFFFFFF mentre -1 in un lungo è 0xFFFFFFFFFFFFFFFF. Per conservare l'identità si applica una maschera dopo la conversione.
| = O
Imposta bit. I bit verranno impostati indipendentemente se sono già impostati. Molte strutture dati (campi di bit) hanno flag come IS_HSET = 0, IS_VSET = 1 che possono essere impostati indipendentemente. Per impostare i flag, si applica IS_HSET | IS_VSET (in C e assembly è molto comodo da leggere)
^ = XOR
Trova bit uguali o diversi.
~ = NOT
capovolgere i bit.
Si può dimostrare che tutto possibili operazioni di bit locali possono essere implementate da queste operazioni. Quindi, se lo desideri, puoi implementare un'istruzione ADD solo tramite operazioni a bit.
Alcuni meravigliosi hack:
http://www.ugcs.caltech.edu/~wnoise/base2.html
http://www.jjj.de/bitwizardry/bitwizardrypage.html
= ~
, no |=
, che è OR.
& = AND
- Perché dovrei voler cancellare tutti i bit, perché dovrei voler ottenere una versione non modificata del byte e cosa devo fare con il nibble inferiore?
xor
da solo. Posso pensare a diversi motivi per cui potresti voler estrarre il bocconcino inferiore. Soprattutto se quel bocconcino inferiore fa parte di una struttura di dati e si desidera utilizzarlo come maschera o OR
con un'altra struttura.
La crittografia è tutte operazioni bit per bit.
Puoi usarli come un modo rapido e sporco per eseguire l'hash dei dati.
int a = 1230123;
int b = 1234555;
int c = 5865683;
int hash = a ^ b ^ c;
Questo è un esempio per leggere i colori da un'immagine bitmap in formato byte
byte imagePixel = 0xCCDDEE; /* Image in RRGGBB format R=Red, G=Green, B=Blue */
//To only have red
byte redColour = imagePixel & 0xFF0000; /*Bitmasking with AND operator */
//Now, we only want red colour
redColour = (redColour >> 24) & 0xFF; /* This now returns a red colour between 0x00 and 0xFF.
Spero che questo piccolo esempio aiuti ...
Nel mondo astratto del linguaggio moderno di oggi, non troppi. Il file IO è semplice che viene in mente, anche se sta esercitando operazioni bit per bit su qualcosa di già implementato e non sta implementando qualcosa che utilizza operazioni bit per bit. Tuttavia, come semplice esempio, questo codice dimostra la rimozione dell'attributo di sola lettura su un file (in modo che possa essere utilizzato con un nuovo FileStream che specifica FileMode.Create) in c #:
//Hidden files posses some extra attibutes that make the FileStream throw an exception
//even with FileMode.Create (if exists -> overwrite) so delete it and don't worry about it!
if(File.Exists(targetName))
{
FileAttributes attributes = File.GetAttributes(targetName);
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
File.SetAttributes(targetName, attributes & (~FileAttributes.ReadOnly));
File.Delete(targetName);
}
Per quanto riguarda le implementazioni personalizzate, ecco un esempio recente: ho creato un "centro messaggi" per inviare messaggi sicuri da un'installazione della nostra applicazione distribuita a un'altra. Fondamentalmente, è analogo all'e-mail, completo di Posta in arrivo, Posta in uscita, Posta inviata, ecc., Ma ha anche la consegna garantita con le conferme di lettura, quindi ci sono sottocartelle aggiuntive oltre a "Posta in arrivo" e "Inviata". Ciò equivaleva a un requisito per me definire in modo generico cosa "nella posta in arrivo" o "nella cartella inviata". Della cartella inviata, devo sapere cosa viene letto e cosa non letto. Di ciò che non è letto, ho bisogno di sapere cosa è ricevuto e cosa non è ricevuto. Uso queste informazioni per creare una clausola where dinamica che filtra un'origine dati locale e visualizza le informazioni appropriate.
Ecco come viene messo insieme l'enum:
public enum MemoView :int
{
InboundMemos = 1, // 0000 0001
InboundMemosForMyOrders = 3, // 0000 0011
SentMemosAll = 16, // 0001 0000
SentMemosNotReceived = 48, // 0011
SentMemosReceivedNotRead = 80, // 0101
SentMemosRead = 144, // 1001
Outbox = 272, //0001 0001 0000
OutBoxErrors = 784 //0011 0001 0000
}
Vedi cosa fa questo? Andando (&) con il valore enum "Posta in arrivo", InboundMemos, so che InboundMemosForMyOrders è nella posta in arrivo.
Ecco una versione ridotta del metodo che crea e restituisce il filtro che definisce una vista per la cartella attualmente selezionata:
private string GetFilterForView(MemoView view, DefaultableBoolean readOnly)
{
string filter = string.Empty;
if((view & MemoView.InboundMemos) == MemoView.InboundMemos)
{
filter = "<inbox filter conditions>";
if((view & MemoView.InboundMemosForMyOrders) == MemoView.InboundMemosForMyOrders)
{
filter += "<my memo filter conditions>";
}
}
else if((view & MemoView.SentMemosAll) == MemoView.SentMemosAll)
{
//all sent items have originating system = to local
filter = "<memos leaving current system>";
if((view & MemoView.Outbox) == MemoView.Outbox)
{
...
}
else
{
//sent sub folders
filter += "<all sent items>";
if((view & MemoView.SentMemosNotReceived) == MemoView.SentMemosNotReceived)
{
if((view & MemoView.SentMemosReceivedNotRead) == MemoView.SentMemosReceivedNotRead)
{
filter += "<not received and not read conditions>";
}
else
filter += "<received and not read conditions>";
}
}
}
return filter;
}
Estremamente semplice, ma un'implementazione ordinata a un livello di astrazione che in genere non richiede operazioni bit per bit.
La codifica Base64 è un esempio. La codifica Base64 viene utilizzata per rappresentare i dati binari come caratteri stampabili per l'invio tramite sistemi di posta elettronica (e altri scopi). La codifica Base64 converte una serie di byte a 8 bit in indici di ricerca dei caratteri a 6 bit. Le operazioni con i bit, lo spostamento e il "o" o "niente" sono molto utili per implementare le operazioni sui bit necessarie per la codifica e la decodifica Base64.
Questo ovviamente è solo 1 degli innumerevoli esempi.
Sono sorpreso che nessuno abbia scelto la risposta ovvia per l'era di Internet. Calcolo di indirizzi di rete validi per una sottorete.
Di solito le operazioni bit a bit sono più veloci rispetto alla moltiplicazione / divisione. Quindi, se hai bisogno di moltiplicare una variabile x per dire 9, lo farai x<<3 + x
che sarebbe qualche ciclo più veloce di x*9
. Se questo codice si trova all'interno di un ISR, risparmierai sui tempi di risposta.
Allo stesso modo, se si desidera utilizzare un array come una coda circolare, sarebbe più veloce (e più elegante) gestire assegni circolari con operazioni poco sagge. (la dimensione dell'array dovrebbe essere una potenza di 2). Ad esempio: è possibile utilizzare tail = ((tail & MASK) + 1)
invece di tail = ((tail +1) < size) ? tail+1 : 0
, se si desidera inserire / eliminare.
Inoltre, se si desidera che un flag di errore contenga più codici di errore, ogni bit può contenere un valore separato. Puoi AND con un singolo codice di errore come controllo. Questo è usato nei codici di errore Unix.
Anche una bitmap n-bit può essere una struttura dati davvero interessante e compatta. Se si desidera allocare un pool di risorse di dimensione n, è possibile utilizzare un n-bit per rappresentare lo stato corrente.
Nessuno sembra aver menzionato la matematica a virgola fissa.
(Sì, sono vecchio, ok?)
Un numero ha x
una potenza di 2? (Utile ad esempio negli algoritmi in cui un contatore viene incrementato e un'azione deve essere eseguita solo il numero logaritmico di volte)
(x & (x - 1)) == 0
Qual è il bit più alto di un numero intero x
? (Questo ad esempio può essere usato per trovare la potenza minima di 2 che è maggiore di x
)
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return x - (x >>> 1); // ">>>" is unsigned right shift
Qual è il 1
bit più basso di un numero intero x
? (Aiuta a trovare il numero di volte divisibile per 2.)
x & -x
x & -x
.
Gli operatori bit a bit sono utili per le matrici in loop di lunghezza pari a 2. Come molte persone hanno menzionato, gli operatori a bit sono estremamente utili e vengono utilizzati in Flag , Grafica , Networking , Crittografia . Non solo, ma sono estremamente veloci. Il mio uso preferito personale è di eseguire il loop di un array senza condizionali . Supponiamo di avere un array basato su indice zero (ad es. L'indice del primo elemento è 0) e che è necessario eseguirne il ciclo indefinitamente. Con indefinitamente intendo passare dal primo elemento all'ultimo e tornare al primo. Un modo per implementare questo è:
int[] arr = new int[8];
int i = 0;
while (true) {
print(arr[i]);
i = i + 1;
if (i >= arr.length)
i = 0;
}
Questo è l'approccio più semplice, se si desidera evitare un'istruzione if , è possibile utilizzare l' approccio del modulo in questo modo:
int[] arr = new int[8];
int i = 0;
while (true) {
print(arr[i]);
i = i + 1;
i = i % arr.length;
}
Il lato negativo di questi due metodi è che l'operatore del modulo è costoso, poiché cerca un resto dopo la divisione dei numeri interi. E il primo metodo esegue un'istruzione if su ogni iterazione. Con l'operatore bit a bit, tuttavia, se la lunghezza dell'array è una potenza di 2, è possibile generare facilmente una sequenza come 0 .. length - 1
utilizzando l' &
operatore (bit a bit e) in questo modo i & length
. Sapendo questo, il codice dall'alto diventa
int[] arr = new int[8];
int i = 0;
while (true){
print(arr[i]);
i = i + 1;
i = i & (arr.length - 1);
}
Ecco come funziona. Nel formato binario ogni numero che è potenza di 2 sottratto da 1 viene espresso solo con quelli. Ad esempio 3 in binario è 11
, 7 è 111
, 15 è 1111
e così via, si ottiene l'idea. Ora, cosa succede se si &
dispone di un numero qualsiasi rispetto a un numero composto solo da quelli binari? Diciamo che facciamo questo:
num & 7;
Se num
è minore o uguale a 7, il risultato sarà num
perché ogni bit&
- con 1 è esso stesso. Se num
è maggiore di 7, durante l' &
operazione il computer considererà gli zeri iniziali di 7 che ovviamente rimarranno come zeri dopo l' &
operazione rimarrà solo la parte finale. Come in caso di 9 & 7
binario sembrerà
1001 & 0111
il risultato sarà 0001 che è 1 in decimale e indirizza il secondo elemento nell'array.
può anche essere utile in un modello relazionale sql, diciamo che hai le seguenti tabelle: BlogEntry, BlogCategory
tradizionalmente potresti creare una relazione tra loro usando una tabella BlogEntryCategory o quando non ci sono molti record BlogCategory che potresti usare un valore in BlogEntry per collegarti a più record BlogCategory proprio come faresti con gli enumerati, nella maggior parte dei RDBMS ci sono anche un operatore molto veloce per selezionare su quella colonna "segnalata" ...
Quando si desidera modificare solo alcuni bit delle uscite di un microcontrollore, ma il registro in cui scrivere è un byte, si fa qualcosa del genere (pseudocodice):
char newOut = OutRegister & 0b00011111 //clear 3 msb's
newOut = newOut | 0b10100000 //write '101' to the 3 msb's
OutRegister = newOut //Update Outputs
Naturalmente, molti microcontrollori ti consentono di cambiare ogni bit singolarmente ...
Se vuoi mai calcolare il tuo numero mod (%) una certa potenza di 2, puoi usare yourNumber & 2^N-1
, che in questo caso è lo stesso di yourNumber % 2^N
.
number % 16 = number & 15;
number % 128 = number & 127;
Questo è probabilmente utile solo in alternativa al funzionamento del modulo con un dividendo molto grande che è 2 ^ N ... Ma anche in questo caso il suo aumento di velocità rispetto all'operazione del modulo è trascurabile nel mio test su .NET 2.0. Sospetto che i compilatori moderni eseguano già ottimizzazioni come questa. Qualcuno sa di più su questo?
%
l'operazione Remainder, trattano i negativi in modo diverso. Tuttavia, se si passa uint
a %
, il compilatore C # produrrà effettivamente il codice macchina usando bit-bit AND quando il secondo argomento è una potenza nota di due.
C'è un uso del mondo reale nella mia domanda qui -
Rispondere solo alla prima notifica WM_KEYDOWN?
Quando si consuma un messaggio WM_KEYDOWN nel bit API 30 di Windows, viene specificato lo stato della chiave precedente. Il valore è 1 se la chiave è inattiva prima dell'invio del messaggio, oppure è zero se la chiave è attiva
Sono principalmente usati per operazioni bit a bit (sorpresa). Ecco alcuni esempi reali trovati nella base di codice PHP.
Codifica dei caratteri:
if (s <= 0 && (c & ~MBFL_WCSPLANE_MASK) == MBFL_WCSPLANE_KOI8R) {
Strutture dati:
ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
Driver di database:
dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY);
Implementazione del compilatore:
opline->extended_value = (opline->extended_value & ~ZEND_FETCH_CLASS_MASK) | ZEND_FETCH_CLASS_INTERFACE;
Ogni volta che ho iniziato la programmazione in C, ho capito le tabelle di verità e tutto il resto, ma non è stato sufficiente fare clic su come utilizzarlo fino a quando non ho letto questo articolo http://www.gamedev.net/reference/articles/article1563.asp (che fornisce esempi di vita reale)
x == 1
e y == 2
, quindi x || y
restituisce 1 e x | y
restituisce 0. Non vedo perché x^true
sia superiore a !x
in alcun modo. È più dattiloscritto, meno idiomatico, e se x
non bool
lo è è inaffidabile.
x^true
è superiore a !x
è some->complicated().member->lookup ^= true;
Non ci sono versioni composto-assegnazione degli operatori unari.
Non penso che questo valga come bit a bit, ma l'array di ruby definisce le operazioni di set attraverso i normali operatori bit a bit di interi. Così [1,2,4] & [1,2,3] # => [1,2]
. Allo stesso modo per a ^ b #=> set difference
e a | b #=> union
.
La soluzione lineare della Torre di Hanoi utilizza operazioni bit per risolvere il problema.
public static void linear(char start, char temp, char end, int discs)
{
int from,to;
for (int i = 1; i < (1 << discs); i++) {
from = (i & i-1) % 3;
to = ((i | i-1) + 1) % 3;
System.out.println(from+" => "+to);
}
}
La spiegazione di questa soluzione può essere trovata qui