javascript-internet-of-things operatori-bit-a-bit

JavaScript, IoT e operatori bitwise

operatori-bitwise

Ne abbiamo già parlato in Linguaggi per il Web; l'evoluzione di JavaScript dell'ultimo decennio lo ha portato a trovare applicazione in ambiti estranei alle finalità dei suoi albori, andando oltre il browser dell'utente e ponendosi quale opzione anche per lo sviluppo back-end (lato-server), mobile, desktop, fino addirittura ad approdare al mondo dell'IoT (Internet of Things ), ovvero di tutto ciò che inizia per "smart"; smart home (domotica), smart tv, smart watch (wearable), smart car, ossia dispositivi intelligenti che inviano dati e informazioni attraverso internet.

La spendibilità di JS nell'IoT è stata merito di framework e tecnologie (si pensi a NodeJS) in grado di interagire con l'hardware (ad es. le schede Arduino o Raspberry) e lanciare dei comandi, dalla semplice accensione di un LED al rilevamento della temperatura di un ambiente.
Tuttavia, per interfacciarsi con un microcontrollore in maniera rapida questo dovrà interpretare ed eseguire codice altrettanto rapido, caratteristica del linguaggio C, nato proprio per lavorare a "basso livello", a stretto contatto con l'hardware.

JavaScript, pur essendo derivato dal C, non ha le stesse caratteristiche, soprattutto in termini di efficienza, proprio per questo, qualora ci si ritrovi ad adoperarlo sulle componenti di un circuito, bisognerà preoccuparsi di scrivere codice che garantisca buone prestazioni quanto a velocità.

A questo proposito, JS eredita dal C le operazioni bit a bit (bitwise ), tipiche negli ambienti a basse prestazioni per l'esiguo consumo di risorse e per la loro velocità, significativamente superiore rispetto alle operazioni aritmetiche classiche (divisioni, moltiplicazioni, addizioni).
Il ricorso ad esse nello sviluppo web è alquanto raro e limitato, molto più diffuso invece (soprattutto in altri linguaggi) in campi dove la manipolazione del singolo bit può fare la differenza, quali:

Gli operatori bitwise

Gli operatori bitwise trattano gli operandi come numeri interi a 32 bit piuttosto che come decimali; dunque, eseguono le operazioni richieste su tali rappresentazioni binarie (0 e 1) per poi restituire il risultato riconvertito in un numero a 64 bit, ovvero un valore decimale.
I bit del primo operando sono abbinati ai corrispondenti del secondo, pertanto l'operatore viene applicato a ciascuna coppia di bit fino a costruire un risultato bit dopo bit.
A differenza degli operatori aritmetici, che lavorano con byte (gruppo di 8 bit) e gruppi di byte, gli operatori bitwise possono controllare ciascuno dei singoli bit all'interno di un byte.
JS mette a disposizione sette operatori bitwise:

  1. & AND;
  2. | OR;
  3. ^ XOR;
  4. ~ NOT;
  5. << Left shift;
  6. >> Right shift;
  7. >>> Unsigned right shift

AND

AND (&) restituisce 1 in ogni posizione in cui i bit corrispondenti di entrambi gli operandi sono 1.
La tabella di verità per l'operazione AND è la seguente:

Operazione
Risultato
0 & 0
0
1 & 1
1
0 & 1
0
1 & 0
0

Per trasformare un intero decimale nella sua rappresentazione binaria, possiamo ricorrere al metodo toString(), il quale generalmente non prevede parametri.
Pertanto, se non passiamo alcun parametro al metodo il numero verrà convertito in una stringa decimale con base 10; invece, se passiamo una base come parametro il decimale verrà convertito in stringa secondo la base definita, nel nostro caso 2, quindi una stringa binaria.


const primo_operando = 11; primo_operando.toString(2);
const secondo_operando = 7; secondo_operando.toString(2);
document.write("11 & 7 = " + primo_operando & secondo_operando);

// 11 =     00000000000000000000000000001011
// 7 =      00000000000000000000000000000111

// 11 & 7 = 00000000000000000000000000000011 (base 2) = 3 (base 10)

Per la conversione da base binaria a decimale, invece, possiamo servirci del metodo parseInt() il quale, come il metodo precedente, accetta un parametro indicante la base numerica alla quale viene applicato.
Nota. Qualora la rappresentazione binaria non dovesse essere una stringa, JavaScript la convertirà in automatico in un numero, applicandole implicitamente il metodo toString().


let binario = "1011"; parseInt(binario, 2); // 11
binario = 1011; parseInt(binario, 2); // 11

OR

OR (|) restituisce 1 in ogni posizione in cui almeno uno dei bit corrispondenti di entrambi gli operandi è 1.
La tabella di verità per l'operazione OR è la seguente:

Operazione
Risultato
0 | 0
0
1 | 1
1
0 | 1
1
1 | 0
1


11 =     00000000000000000000000000001011
7 =      00000000000000000000000000000111

11 | 7 = 00000000000000000000000000001111 (base 2) = 15 (base 10)

XOR

XOR (^) restituisce 1 in ogni posizione in cui i bit corrispondenti di entrambi gli operandi sono diversi.
La tabella di verità per l'operazione XOR è la seguente:

Operazione
Risultato
0 ^ 0
0
1 | 1
0
0 | 1
1
1 | 0
1


11 =     00000000000000000000000000001011
7 =      00000000000000000000000000000111

11 ^ 7 = 00000000000000000000000000001100 (base 2) = 12 (base 10)

NOT

NOT (~) inverte i bit dell'operando al quale viene applicato, restituendo un numero la cui rappresentazione binaria ha un 1 in ogni posizione in cui il bit corrispondente dell'operando è 0, più 1, che identifica il bit più significativo all'estrema sinistra, utilizzato per esprimere numeri interi negativi.


~ primo_operando = -(11 + 1)
11 =   00000000000000000000000000001011
~ 11 = 11111111111111111111111111110100 (base 2) = -12 (base 10)

Nota. Attenzione a non confondere l'operatore NOT con il procedimento per ottenere un decimale di segno negativo, data la similitudine; in questo caso, l'operazione da eseguire è la medesima se non per l'1, che andrà aggiunto all'estrema destra della rappresentazione.


11 =  00000000000000000000000000001011
-11 = 11111111111111111111111111110101

Left shift

Left shift (<<) opera sulla rappresentazione in bit dell'operando di sinistra, spostando di b posizioni a sinistra la rappresentazione binaria di a, inserendo i bit 0 da destra ed eliminando quelli in eccesso a sinistra; l'equivalente di a * (2 ** b).


11 =      00000000000000000000000000001011
11 << 7 = 00000000000000000000010110000000 (base 2) = 1408 (base 10)

Right shift

Il funzionamento dell'operatore Right shift (>>) è analogo al precedente, ma lo spostamento di b posizioni della rappresentazione binaria di a avviene verso destra, inserendo i bit 0 da sinistra ed eliminando quelli in eccesso a destra.


11 =      00000000000000000000000000001011
11 >> 1 = 00000000000000000000000000000101 (base 2) = 5 (base 10)

Unsigned right shift

Anche il meccanismo di Unsigned right shift (>>>) è simile al precedente, se non per il bit corrispondente al segno, che diventa 0, quindi il numero risultante sarà sempre positivo, anche se l'operando di partenza è negativo.
Ecco perché viene chiamato unsigned (senza segno), a differenza dei due precedenti in cui il bit all'estrema sinistra del nuovo operando ha lo stesso valore del bit situato nella stessa posizione dell'operando al quale viene applicato l'operatore, e per questo chiamati sign-propagating (con propagazione del segno).


11 =        00000000000000000000000000001011
11 >> 1 =   00000000000000000000000000000101 (base 2) = 5 (base 10)
11 >>> 1 =  00000000000000000000000000000101 (base 2) = 5 (base 10)

-11 =       11111111111111111111111111110101
-11 >>> 1 = 01111111111111111111111111111101 (base 2) = 2147483642 (base 10)

L'esempio dimostra come Right shift e Unsigned right shift producono lo stesso risultato sui numeri positivi, non altrettanto sui negativi.

Un'ultimo ma non meno importante chiarimento va fatto sulla conversione da binario a decimale di un numero negativo.
Per compierla correttamente andrà applicato l'operatore precedente, Unsigned right shift, che tratta i suoi operandi come numeri interi senza segno, 1 nel caso del meno (-).
Eseguendo il passaggio da decimale a binario su numeri negativi senza l'intervento della suddetta operazione di spostamento, il metodo toString() si limiterà ad aggiungere il segno - prima del numero intero, non restituendo la stringa binaria effettiva del numero negativo, ma bensì la rappresentazione positiva preceduta dal segno.


let decimale = 11; decimale.toString(2) // 1011
decimale = -11; decimale.toString(2) // -1011
(decimale >>> 0).toString(2); // 11111111111111111111111111110101

Privacy Policy