In questo breve articolo si parlerà di come PHP fornisca degli strumenti per lavorare con concetti come gli operatori bitwise (operatori per l'alterazione dei bits) e la matematica binaria; data la scarsa simpatia che molti nutrono per i numeri e i calcoli, questo argomento viene spesso tralasciato, può però risultare molto utile ai fini della comprensione dei linguaggi di sviluppo, perché possedere qualche base relativa alla matematica binaria significa anche capire come pensano le macchine.
Matematica binaria: pensare in base 2
Non occorre dilungarsi eccessivamente per spiegare il concetto di matematica binaria; per convenzione le persone sono abituate ad effettuare calcoli su base decimale: abbiamo 10 numeri che vanno da 0 a 9 e tutte le cifre esistenti su questa scala si basano sulla combinazione di questi; per cui un numero casuale come "587" (5 = centinaia, 8 = decine, 7 = unità) non è altro che il risultato di:
(5 * 102) + (8 * 101) + (7 * 100) = 500 + 80 + 7
Ma immaginiamo che i numeri possano essere composti esclusivamente da due cifre, lo "0" e l'"1", in questo caso la base della nostra scala numerica sarebbe il 2 e non più il 10; per cui potremmo avere a che fare con cifre come "1101" o "1111"; per quanto possa sembrare strano, questi numeri hanno una corrispondenza su base 10 e quindi possono essere tradotti in cifre comunemente utilizzate per i consueti calcoli matematici. Facciamo un esempio:
1110 = (1 * 23) + (1 * 22) + (1 * 21) + (0 * 20) = 8 + 4 + 2 + 0 = 14
Seguendo la stessa logica avremo che il numero binario "1101" è pari a 13 e che "1111" è pari a 15; grazie al sistema a base 2 è possibile rappresentare tutte le cifre possibili utilizzando combinazioni costituite da due soli simboli.
Matematica binaria e bits
Per questa sua caratteristica di semplicità gli informatici hanno deciso di utilizzare la base 2 come fondamento per il linguaggio delle macchine che, come è noto, interpretano i dati sulla base dei concetti di VERO ("1" o TRUE) e FALSO ("2" o FALSE).
Vero e falso possono essere anche dei riferimenti ad uno stato, si pensi per esempio al bit che di per sé non è altro che la rappresentazione di "1" e "0" corrispondenti agli stati di On e Off, un concetto elaborato sulla base del fatto che ogni computer viene alimentato tramite la corrente elettrica; 8 bits formano un byte che ha un valore massimo di "255" ("11111111" in base 2) sempre basato sulla matematica binaria, infatti ad ognuno degli 8 bit è associato un valore:
- 128 = (1 * 27);
- 64 = (1 * 26);
- 32 = (1 * 25);
- 16 = (1 * 24);
- 8 = (1 * 23);
- 4 = (1 * 22);
- 2 = (1 * 21);
- 1 = (1 * 20).
La somma di questi valori è appunto 255; il valore massimo si ottiene soltanto se tutti e gli 8 bits del byte sono impostati su On ("1"), altrimenti il valore del byte diminuirà. Se per esempio il bit corrispondente a 128 è impostato su Off, avremo per il byte una cifra binaria pari a "01111111" che corrisponde a "127" su base decimale.
I valori che compongono i bytes e quindi i bits che li costituiscono sono manipolabili impostando questi ultimi su On o su Off, ciò può essere fatto utilizzando diversi strumenti, uno di questi sono gli operatori bitwise di PHP.
Operatori bitwise in PHP: quali sono e a cosa servono
Come abbiamo anticipato nel precedente paragrafo, gli operatori bitwise sono degli strumenti che consentono di alterare bits specifici in posizione On oppure Off, si tratta di operazioni non da poco in quanto permettono di analizzare la struttura stessa dei dati.
Gli operatori bitwise che possiamo utilizzare in PHP sono i seguenti:
- $var1 & $var2 (And): vengono impostati su On i bits che sono On sia per $var1 che per $var2;
- $var1 | $var2 (Or): vengono impostati su On i bits che sono On per $var1 oppure per $var2;
- $var1 ^ $var2 (Xor): vengono impostati su On i bits che sono On per $var1 oppure per $var2 ma non quelli che sono entrambi On;
- ~ $var1 (Not): vengono impostati su On i bits che sono su Off in $var1, e viceversa;
- $var1 << $var2 (Shift left): vengono spostati i bits appartenenti a $var1 sulla sinistra di un numero di passi pari a $var2, ogni passo corrisponderà ad una moltiplicazione per "2";
- $var1 >> $var2 (Shift right): vengono spostati i bits appartenenti a $var1 sulla destra di un numero di passi pari a $var2, ogni passo corrisponderà ad una divisione per "2".
Un semplice esempio chiarirà quanto esposto fino ad ora; si consideri l'operatore And e lo si utilizzi nel seguente costrutto:
<?php $var1 = 33; $var2 = 34; echo $var1 & $var2; ?>
Eseguendo questo scriptino nella Root del Web Server riceveremo in output la cifra "32", perché accade questo? Perché "33" corrisponde alla cifra in base 2 "00100001", mentre "34" corrisponde a "00100010"; questo significa che entrambi hanno impostato su On il bit con valore pari a "32", quindi il risultato dell'azione dell'operatore sarà appunto questa cifra in quanto con And vengono impostati su On i bits che sono On sia per $var1 che per $var2.
In altre parole, "33" è il risultato della somma tra il bit con valore "32" in On più il bit con valore "1" in On, "34" è invece il risultato della somma tra il bit con valore "32" in On più il bit con valore "2" in On; ma "1" e "2" non sono On per entrambi, quindi l'utilizzo dell'operatore si limiterà a restituire il valore "32" che è On sia per $var1 che per $var2.
Nel caso più di un bit sia On per entrambe le variabili, allora il risultato sarà pari alla somma del loro valore.
Si analizzi un secondo esempio basato sull'operatore Or:
<?php $var1 = 33; $var2 = 34; echo $var1 | $var2; ?>
In questo caso l'output prodotto sarà pari a "35", con Or infatti vengono presi in considerazione i bits su On per $var1 oppure per $var2, per cui "35" è il risultato della sommatoria "32+2+1" cioè del totale dei valori dei bits che sono impostati su On; dato che le diverse combinazioni dei valori associati ai bits possono produrre soltanto valori univoci, questo operatore ci permette di conoscere quali bits sono impostati su On per una determinata cifra.
Si passi ora ad analizzare l'operatore Xor:
<?php $var1 = 33; $var2 = 34; echo $var1 ^ $var2; ?>
L'espressione appena proposta produrrà un output pari a "3", con questo operatore infatti sono presi in considerazione i bits su On per $var1 oppure per $var2 ma non quelli che sono On per entrambi; dato che sia per "33" che per "34" è On il bit che ha valore "32", questo non parteciperà alla generazione dell'output, mentre i valori non ripetuti in entrambi come "1" e "2" verranno sommati e genereranno il risultato finale.
Altro discorso è quello relativo all'operatore NOT:
<?php $var1 = 33; $var2 = 34; echo $var1 & ~$var2; ?>
Il risultato dello script proposto sarà la stampa a video della cifra "1", l'operatore NOT infatti effettua un'analisi delle variabili utilizzate come operandi per sapere quali bits sono stati impostati su On in $var1 e non in $var2 (per questo il simbolo della tilde precede la variabile $var2), dalla ricerca risulterà quindi che l'unico bit impostato su On per $var1 che non è su On anche per $var2 è quello con valore pari a "1".
Il risultato sarebbe stato naturalmente pari a "2" se la "~" avesse preceduto la variabile $var1 invece che $var2.
Passiamo ora all'analisi dell'ultima tipologia di operatori:
<?php $var1 = 8; $var2 = 2; echo $var1 << $var2; ?>
L'utilizzo dell'operatore Shift left sulla variabile a cui è stato associato un valore pari a "8", produrrà in output la cifra "32", infatti, con "<<" i bits appartenenti ad $var1 vengono spostati sulla sinistra di un numero di passi pari al valore associato a $var2; dato che $var2 vale "2" e ogni passo equivale ad una moltiplicazione per "2", il risultato sarà quindi pari a "8 * 2 * 2 = 32".
Se di fosse utilizzato invece l'operatore Shift right:
echo $var1 >> $var2;
Il risultato sarebbe stato invece pari a "2", in quanto i due gradini successivi all'"8" sono appunto il "4" e il "2" ("(8/2)/2 = 2").
Operatori bitwise in PHP e stringhe
Quando vengono utilizzati gli operatori bitwise di PHP, nel caso in cui entrambi i parametri posti sulla sinistra e sulla destra dell'espressione siano delle stringhe, l'operazione verrà effettuata a carico dei caratteri di questa stringhe ed in particolare sui loro valori ASCII.
Per ASCII (American Standard Code for Information Interchange, letteralmente: Codice Standard Americano per lo Scambio di Informazioni), si intende un sistema di codifica dei caratteri comunemente utilizzato nei calcolatori; in pratica per ogni carattere utilizzato esiste una corrispondente rappresentazione numerica denominata codice ASCII (per esempio, la "@" corrisponde al codice ASCII "64").
Si analizzi l'esempio seguente:
<?php // genera l'output "5" echo 12 ^ 9; // genera in output un backspace echo "12" ^ "9"; ?>
Nello specifico:
- la prima espressione restituisce in output "5", infatti "12" corrisponde al valore in base 2 "00001100" ("8+4") mentre "9" corrisponde a "00001001" ("8+1"); 5 è quindi il risultato della somma tra i valori non presenti in entrambi "4+1";
- la seconda espressione restituisce in output una Backspace, essa corrisponde infatti al codice ASCII "#8" ottenuto tramite l'utilizzo dell'operatore sulle cifre "1" (codice ASCII 49 e numero binario '00000001') e 9 (codice ASCII 57 e binario "00002001"); 8 è quindi l'unico valore di bit non disponibile per entrambi i parametri.
Conclusioni
Gli operatori Bitwise sono degli strumenti poco conosciuti ma molto interessanti che PHP mette a disposizione per la manipolazione della struttura interna dei bytes e per l'analisi dello stato dei bits che li compongono; in questa breve trattazione abbiamo analizzato le diverse tipologie di operatori, la sintassi necessaria per il loro utilizzo e il loro rapporto con la matematica binaria utilizzata diffusamente in informatica.