Cosa fanno le operazioni bit a bit
Un’operazione bit a bit tratta un numero come una fila di bit e lavora ogni posizione in modo indipendente. Sono le operazioni più veloci che una CPU abbia e compaiono ovunque: flag di permessi, maschere di rete, funzioni hash, grafica, compressione e analisi di protocolli.
| Operazione | Simbolo (C, JS, Go, Python) | Regola per bit | Uso tipico |
|---|---|---|---|
| AND | & | 1 solo se entrambi i bit sono 1 | Mascherare bit, verificare un flag: x & FLAG |
| OR | | | 1 se uno dei due bit è 1 | Impostare un flag: x | FLAG |
| XOR | ^ | 1 se i bit sono diversi | Invertire un flag, scambiare, checksum |
| NOT | ~ | Inverte ogni bit | Costruire una maschera inversa: x & ~FLAG azzera un flag |
| Scorrimento a sinistra | << | Sposta i bit a sinistra, riempie con 0 | Moltiplicare per 2ⁿ, costruire una maschera: 1 << n |
| Scorrimento a destra | >> | Sposta i bit a destra | Dividere per 2ⁿ, estrarre un campo |
| Rotazione | rotl / rotr | Scorre; i bit che escono rientrano dall’altra estremità | Hash e cifrari |
Scorrimento a destra aritmetico e logico
Lo scorrimento a destra esiste in due varianti. Uno scorrimento logico riempie i nuovi bit alti con zeri. Uno aritmetico copia il bit di segno, così un numero negativo in complemento a due resta negativo. In C e Go, >> su un tipo con segno è aritmetico e su uno senza segno è logico. JavaScript li ha entrambi: >> è aritmetico, >>> logico. Imposta qui la larghezza a 8 e prova -8 >> 1 per vedere la differenza.
Perché la larghezza conta
In un registro reale, i bit spostati oltre l’estremità alta vanno persi, e NOT inverte esattamente tanti bit quanti ne ha il registro. ~0 è 255 a 8 bit, 65535 a 16 e 4294967295 a 32. Questo calcolatore lavora sempre nella larghezza scelta, così i risultati corrispondono a ciò che il tuo codice vede in un uint8_t, uint32 e così via.
Idiomi comuni
Domande
Qual è la differenza tra operatori logici e bit a bit?
Gli operatori logici (&&, ||, !) trattano valori interi come vero o falso e restituiscono un singolo vero/falso. Gli operatori bit a bit (&, |, ^, ~) lavorano su ogni bit degli operandi e restituiscono un numero. 6 && 3 è vero, ma 6 & 3 è 2.
Perché NOT restituisce un numero negativo nel mio codice?
Se la variabile ha il segno, il bit più alto è il bit di segno. ~5 in complemento a due a 32 bit è 0xFFFFFFFA, che un int con segno mostra come −6. Salva il risultato in un tipo senza segno o mascheralo (~5 & 0xFF = 250) per vedere lo schema di bit grezzo.
Che succede ai bit spostati oltre l’estremità?
In un registro a larghezza fissa vengono scartati. Lo scorrimento a sinistra può quindi perdere i bit alti (overflow); quello a destra perde i bit bassi (per questo x >> 1 arrotonda per difetto). Le rotazioni sono diverse: i bit rientrano dall’altra estremità.
Lo XOR di un numero con sé stesso dà sempre zero?
Sì. Ogni bit viene confrontato con un bit identico, e lo XOR di bit uguali è 0. Per questo x ^ x = 0, e per questo lo XOR può trovare l’unico elemento spaiato in una lista in cui tutto il resto compare due volte.
Posso usarlo per valori a 64 bit?
Sì. Lo strumento calcola con interi a precisione arbitraria e maschera alla larghezza scelta, quindi le operazioni a 64 bit sono esatte, a differenza degli operatori nativi di JavaScript, che si fermano a 32 bit.