A cosa servono gli operatori bit? [chiuso]


19

I linguaggi di programmazione vengono spesso con vari operatori di bit (ad es. Spostamento bit a destra e sinistra, bit a bit AND, OR, XOR ...). Questi non si abituano molto, o almeno così è stata la mia esperienza. A volte vengono utilizzati nelle sfide di programmazione o nelle domande di intervista o la soluzione potrebbe richiederli, ad esempio:

  • Senza utilizzare alcun operatore di uguaglianza, creare una funzione che ritorni truequando due valori sono uguali
  • Senza utilizzare una terza variabile, scambiare il valore di due variabili

Questi poi di nuovo, probabilmente hanno pochi usi nel mondo reale . Immagino che dovrebbero essere più veloci perché manipolano direttamente la memoria a basso livello.

Perché si trovano nella maggior parte dei linguaggi di programmazione? Qualche caso d'uso reale?


@Anto - Un semplice esempio potrebbe essere l'invio di un valore di 256 KB di dati a una velocità di 256 parole alla volta (4096 byte) a un client.
Ramhound,

1
"Senza usare alcun operatore di uguaglianza, creare una funzione che ritorni vero quando due valori sono uguali" - in C return !(x-y);:? Non so
Andrew Arnold,

@Andrew: questa è una soluzione, ma puoi farlo anche con operatori bit a bit.
Anto,

19
"Questi non si abituano molto anche se" - Sicuro? Li uso sempre. Non tutti lavoriamo nel tuo dominio problematico.
Ed S.

2
Non abbastanza per una risposta completa, ma prova a leggere i primi 4 bit di un byte senza manipolare i bit e poi considera che alcuni formati di dati sono molto stretti.
Brendan Long,

Risposte:


53

No, hanno molte applicazioni del mondo reale e sono operazioni fondamentali sui computer.

Sono usati per

  • Giocoleria di blocchi di byte che non rientrano nei tipi di dati dei linguaggi di programmazione
  • Commutazione della codifica avanti e indietro da big a little endian.
  • Imballaggio di 4 pezzi a 6 bit di dati in 3 byte per una connessione seriale o USB
  • Molti formati di immagine hanno quantità diverse di bit assegnati a ciascun canale di colore.
  • Tutto ciò che coinvolge pin IO nelle applicazioni integrate
  • Compressione dei dati, che spesso non ha dati che si adattano bene ai confini di 8 bit. \
  • Algoritmi di hash, CRC o altri controlli di integrità dei dati.
  • crittografia
  • Generazione di numeri psuedorandom
  • Raid 5 utilizza XOR bit a bit tra i volumi per calcolare la parità.
  • Molto di più

In effetti, logicamente, tutte le operazioni su un computer alla fine si riducono a combinazioni di queste operazioni bit per bit di basso livello, che si svolgono all'interno delle porte elettriche del processore.


1
+1 per l'elenco piuttosto completo, a cui sembra che tu stia aggiungendo
Anto

28
+1. @Anto: questo elenco non è affatto completo. Un elenco completo di casi d'uso per operatori bit a bit nella programmazione dei sistemi sarebbe lungo quanto un elenco completo per le query SQL nelle applicazioni aziendali. Curiosità: utilizzo sempre operazioni bit per bit, ma non
scrivo

4
@nikie: E scrivo sempre SQL, ma non uso operatori bit per bit da anni! :)
FrustratedWithFormsDesigner

3
Lavoro in sistemi embedded: gli operatori bit a bit sono roba da pane e burro. Usato tutti i giorni senza alcun pensiero.
quick_now

9
Se di tanto in tanto utilizzo il bitshifting in SQL, ricevo un premio?
Formica

13

Perché sono operazioni fondamentali.

Per lo stesso ragionamento, si potrebbe sostenere che l' addizione ha pochi usi nel mondo reale, poiché può essere sostituita completamente con sottrazione (e negazione) e moltiplicazione. Ma continuiamo ad aggiungere perché è un'operazione fondamentale.

E non pensare per un momento che solo perché non hai visto molto bisogno di operazioni bit per bit non significa che non vengano utilizzate molto spesso. In effetti, ho usato operazioni bit per bit in quasi tutte le lingue che ho usato per cose come il mascheramento dei bit.

Dalla parte superiore della mia testa, ho usato operazioni bit per bit per l'elaborazione di immagini, campi di bit e flag, elaborazione di testo (ad esempio, tutti i caratteri di una particolare classe spesso condividono un modello di bit comune), codifica e decodifica di dati serializzati, decodifica di VM o CPU codici operativi e così via. Senza operazioni bit per bit, la maggior parte di queste attività richiederebbe operazioni molto più complesse per eseguire l'attività in modo meno affidabile o con una scarsa leggibilità.

Per esempio:

// Given a 30-bit RGB color value as a 32-bit int
// A lot of image sensors spit out 10- or 12-bit data
// and some LVDS panels have a 10- or 12-bit format
b = (color & 0x000003ff);
g = (color & 0x000ffc00) >> 10;
r = (color & 0x3ff00000) >> 20;

// Going the other way:
color = ((r << 20) & 0x3ff00000) | ((g << 10) & 0x000ffc00) | (b & 0x000003ff);

La decodifica delle istruzioni della CPU per le CPU di tipo RISC (come quando si emula un'altra piattaforma) richiede l'estrazione di porzioni di grande valore come sopra. A volte, fare queste operazioni con moltiplicazione e divisione e modulo, ecc., Può essere fino a dieci volte più lento delle equivalenti operazioni bit per bit.


12

Un esempio tipico è l'estrazione dei singoli colori da un valore RGB a 24 bit e viceversa.


EDIT: da http://www.docjar.com/html/api/java/awt/Color.java.html

    value =  ((int)(frgbvalue[2]*255 + 0.5))    |
                (((int)(frgbvalue[1]*255 + 0.5)) << 8 )  |
                (((int)(frgbvalue[0]*255 + 0.5)) << 16 ) |
                (((int)(falpha*255 + 0.5)) << 24 );

Mostra questo esempio in pratica? Uno snippet di codice?
Anto,

Un esempio migliore potrebbe essere l'elaborazione di valori RGB a 16 bit (4.5bpc) o 30 bit (10bpc).
Greyfade,

@grigio, sentiti libero di aggiungere tali esempi.

6

Ecco un esempio del mondo reale che troverai in Quake 3, Quake 4. Doom III. Tutti quei giochi che utilizzavano il motore Q3 .

float Q_rsqrt( float number )
{
        long i;
        float x2, y;
        const float threehalfs = 1.5F;

        x2 = number * 0.5F;
        y  = number;
        i  = * ( long * ) &y;                       // evil floating point bit level hacking [sic]
        i  = 0x5f3759df - ( i >> 1 );               // what the fuck? [sic]
        y  = * ( float * ) &i;
        y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//    y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

        return y;
}

(Per capire quel codice è necessario capire come sono memorizzati i numeri in virgola mobile, sicuramente non posso elaborarlo)

In termini di utilizzo, a meno che non ci si trovi in ​​campi che richiedono il bit shifting come la rete o la grafica, è possibile che il loro scopo sia leggermente accademico. Ma comunque interessante (per me almeno).


+1 Per quei commenti, anche se non sono tuoi. Mi ha fatto ridere.
Bassinator

4

Lo spostamento è più veloce della moltiplicazione o della divisione per una potenza di due. Ad esempio, a << = 2 moltiplica a per 4. Al contrario, a >> = 2 divide a per quattro. È anche possibile eseguire il bit-bang dei dati su un dispositivo utilizzando gli operatori bit-saggio. Ad esempio, possiamo inviare N flussi di dati seriali da una porta pin N usando le operazioni shift, xor e "e" all'interno di N loop. Tutto ciò che può essere realizzato nella logica digitale può essere realizzato anche su software e viceversa.


1
Fai solo attenzione quando dividi per arrotondamento per eccesso o per difetto ecc. Lo spostamento non tiene conto di ciò, quindi ho trovato effettivamente una pratica migliore usare una divisione nel codice quando intendo una divisione e permetto al compilatore di ottimizzarlo in uno spostamento e aggiungere per me.
Daemin,

@Daemin: sto lavorando con numeri interi quando utilizzo questa tecnica. Il comportamento predefinito per la divisione di numeri interi in C e C ++ è il troncamento verso zero; pertanto, spostare un intero a destra di una potenza di due produce lo stesso risultato della divisione di un intero per una potenza di due.
bit-twiddler,

1
@ bit-twiddler Lo spostamento a destra non funziona allo stesso modo della divisione per i numeri negativi.
Daemin,

@Daemin: sembra che tu sia intenzionato a dimostrarmi che mi sbaglio. Innanzitutto, si presenta il problema di arrotondamento. Quando ripudio questa affermazione affermando che la divisione in C e C ++ viene troncata verso zero, si elimina il numero intero con segno. Dove ho detto che stavo applicando l'operatore di turno ai numeri interi negativi complementi firmati due? Detto questo, si può ancora usare l'operatore di cambio per dividere per una potenza di due. Tuttavia, poiché C e C ++ eseguono e l'aritmetica spostamento a destra anziché un semplice spostamento a destra, si deve prima verificare se il valore è negativo. Se il valore è negativo,
bit-twiddler

1
Esatto, fai attenzione quando usi lo spostamento come sostituto della moltiplicazione e della divisione poiché ci sono sottili differenze. Ne più ne meno.
Daemin,

3

Molto tempo fa, gli operatori bit erano utili. Oggi lo sono di meno. Oh, non sono del tutto inutili, ma è da tanto che non ne vedo uno usato che avrebbe dovuto essere usato.

Nel 1977 ero un programmatore di linguaggio assembly. Ero convinto che l'assemblatore fosse l'unico vero linguaggio. Ero certo che un linguaggio come il Pascal fosse per stranezze accademiche che non dovevano mai fare qualcosa di reale .

Poi ho letto "Il linguaggio di programmazione C" di Kernighan e Ritchie. Mi ha completamente cambiato idea. La ragione? Aveva operatori bit! E ' stato un linguaggio assembly! Aveva solo una sintassi diversa.

A quei tempi non riuscivo a concepire la scrittura di codice senza ands, os, turni e rotazioni. Oggi non li uso quasi mai.

Quindi, la risposta breve alla tua domanda è: "Niente". Ma non è abbastanza giusto. Quindi la risposta più lunga è: "Per lo più niente".


xkcd.com/378 mi viene in mente.
Max

Gli operatori di bit sono utili fino ad oggi. Il fatto che nel tuo dominio non vengano utilizzati non lo rende inutilizzato o addirittura non utilizzato molto spesso. Ecco un semplice esempio: prova ad implementare AES senza operatori di bit. Questo è un esempio fuori dal comune di qualcosa che viene fatto nella maggior parte dei computer su base giornaliera centinaia o migliaia di volte al giorno.
SOLO IL MIO OPINIONE corretta,

La codifica / decodifica dei dati senza l'utilizzo di operatori bit-saggio è nella migliore delle ipotesi dolorosa. Ad esempio, l'aggiunta di allegati MIME a un messaggio richiede che siamo in grado di gestire la codifica dei dati da tre a quattro (ovvero la codifica radix64).
bit-twiddler

2

crittografia

Suggerisco di dare un'occhiata a un frammento molto piccolo dall'algoritmo di crittografia DES :

temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);

Sebbene non sia sicuro DES è raccomandato in questi giorni: P
Armand

@Alison: No, ma gli algoritmi di crittografia che lo hanno sostituito implicano ancora più operazioni di manipolazione dei bit, credo. :-)
Carson63000,

@Alison - certo, ma TripleDES è appena fatto DES 3 volte con 3 volte i bit chiave.
Scott Whitlock,

2

Molte buone risposte, quindi non ripeterò quegli usi.

Li uso parecchio nel codice gestito (C # / .Net), e non ha nulla a che fare con gli algoritmi salvaspazio, ad alte prestazioni o intelligenti di spostamento dei bit. A volte un po 'di logica è adatta per archiviare i dati in questo modo. Li uso spesso quando ho un enum ma le istanze possono prendere simultaneamente più valori da quell'enum. Non riesco a pubblicare un esempio di codice dal lavoro, ma un rapido google per "Flags enum" ("Flags" è il modo C # di definire un enum da usare in modo bit a bit) fornisce questo bell'esempio: http: // www.dotnetperls.com/enum-flags .


2

C'è anche un bit di calcolo parallelo. Se i tuoi dati sono solo 1 e 0, puoi comprimerne 64 in una lunga parola senza segno e ottenere operazioni parallele a 64 vie. Le informazioni genetiche sono due bit (che rappresentano la codifica AGCT del DNA) e se puoi fare i vari calcoli in modo bit parallelo puoi fare molto di più che se non lo fai. Per non parlare della densità dei dati nella memoria, se la memoria, la capacità del disco o la larghezza di banda delle comunicazioni sono limitate, è necessario considerare la compressione / decompressione. Persino gli interi con preclusione bassa, che si presentano in aree come l'elaborazione delle immagini, possono trarre vantaggio dall'elaborazione parallela bit bit. È un'arte intera per sé.


1

Perché sono stati trovati?

Bene, probabilmente perché corrispondono alle istruzioni di assemblaggio e talvolta sono utili solo per cose in linguaggi di livello superiore. Lo stesso vale per il temuto GOTOche corrisponde alle JMPistruzioni di montaggio.

Quali sono i loro usi?

Davvero ci sono solo molti usi per nominare, quindi darò solo un uso recente, sebbene altamente localizzato. Lavoro molto con il 6502 assembly e stavo lavorando su una piccola applicazione che converte indirizzi di memoria, valori, confrontare valori ecc. In codici che possono essere utilizzati per il dispositivo GameGenie (in pratica un'applicazione cheat per NES). I codici sono creati da una manipolazione di bit.


1

Oggi molti programmatori sono abituati a computer con memoria pressoché infinita.

Ma alcuni utili programmano ancora piccoli microcontrollori dove ogni bit conta (quando hai solo 1k o meno RAM per esempio), e gli operatori bit a bit consentono a un programmatore di usare quei bit uno alla volta invece di sprecare una programmazione molto più grande entità di astrazione che potrebbe essere necessaria per mantenere un certo stato richiesto dall'algoritmo. L'IO su questi dispositivi può anche richiedere di essere letto o controllato su base bit a bit.

Il "mondo reale" ha molti più piccoli microcontrollori rispetto a server o PC.

Per i tipi CS teorici puri, le macchine di Turing sono tutte incentrate su bit di stato.


1

Solo uno dei tanti possibili usi degli operatori bit per bit ...

Gli operatori bit a bit possono anche aiutare a rendere il tuo codice più leggibile. Considera la seguente dichiarazione di funzione ....

int  myFunc (bool, bool, bool, bool, bool, bool, bool, bool);

...

myFunc (false, true, false, false, false, true, true, false);

È molto facile dimenticare quale parametro booleano significa cosa quando si scrive o si legge il codice. È anche facile perdere traccia del tuo conteggio. Tale routine può essere ripulita.

/* More descriptive names than MY_FLAGx would be better */
#define MY_FLAG1    0x0001
#define MY_FLAG2    0x0002
#define MY_FLAG3    0x0004
#define MY_FLAG4    0x0008
#define MY_FLAG5    0x0010
#define MY_FLAG6    0x0020
#define MY_FLAG7    0x0040
#define MY_FLAG8    0x0080

int  myFunc (unsigned myFlags);

...

myFunc (MY_FLAG2 | MY_FLAG6 | MY_FLAG7);

Con nomi di flag più descrittivi, diventa molto più leggibile.


1

Se sai qualcosa su Unicode , probabilmente hai familiarità con UTF-8. Usa un sacco di bit test, turni e maschere per impacchettare il punto del codice a 20 bit in 1-4 byte.


0

Non li utilizzo spesso ma a volte sono utili. Mi viene in mente la gestione di Enum .

Esempio:

enum DrawBorder{None = 0, Left = 1, Top = 2, Right = 4, Bottom = 8}

DrawBorder drawBorder = DrawBorder.Left | DrawBorder.Right;//Draw right & left border
if(drawBorder & DrawBorder.Left == DrawBorder.Left)
  //Draw the left border
if(drawBorder & DrawBorder.Top == DrawBorder.Top)
  //Draw the top border
//...

0

Non sono sicuro che questo utilizzo sia stato ancora notato:

Vedo O un bel po 'quando lavoro con il codice sorgente illumos (openSolaris) per ridurre più valori di ritorno a 0 o 1, ad es.

int ret = 0;
ret |= some_function(arg, &arg2); // returns 0 or 1
ret |= some_other_function(arg, &arg2); // returns 0 or 1
return ret;
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.