FUNZIONI BOOLEANE Le funzioni booleane prendono il nome da Boole, che introdusse un formalismo che opera su variabili (dette variabili booleane o variabili logiche o asserzioni) che possono assumere due soli valori: Vero Falso Tale formalismo è detto algebra booleana e nasce come tentativo di definire in forma algebrica processi di tipo logico-deduttivo. Tuttavia, poiché di fatto l’algebra di Boole opera su variabili binarie, che possono cioè assumere solo due valori, è possibile definire una corrispondenza fra i valori logici vero e falso e i valori binari 0 e 1, includendo di fatto gli operatori booleani fra gli operatori dell’algebra binaria. Al valore vero viene normalmente fatto corrispondere il valore binario 1, mentre al valore falso viene fatto corrispondere il valore binario 0. L’algebra booleana è definita su un insieme finito di valori (i due valori vero e falso) che è possibile manipolare attraverso funzioni opportunamente definite. Tali funzioni operano su una o due variabili booleane e producono come risultato un solo valore, anch’esso booleano. Gli operatori di base, con cui è possibile costruire qualunque altra funzione di variabili booleane, sono 3: NOT AND OR (opera su una sola variabile booleana; il risultato è la negazione della variabile) (opera su due variabili booleane; il risultato è vero se entrambe le variabili hanno valore vero) (opera su due variabili booleane; il risultato è vero se almeno una variabile ha valore vero) In realtà è possibile dimostrare che qualunque funzione booleana può essere rappresentata utilizzando solo 2 di questi operatori, di cui uno sia NOT. Operatori e funzioni booleane possono essere descritte attraverso le cosiddette tabelle di verità, che associano a ciascuna possibile combinazione di valori delle variabili il corrispondente valore della funzione. Le tabelle di verità per NOT, AND e OR sono le seguenti: X NOT X X1 X2 X1 AND X2 X1 X2 X1 OR X2 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 1 1 0 0 1 0 1 1 1 1 1 1 1 Si può notare come la tabella di verità di una funzione ad N variabili abbia N+1 colonne, una per ogni variabile di ingresso più una per il valore che deve assumere la funzione. Inoltre, una tabella di verità ha tante righe quante sono le combinazioni possibili che le variabili di ingresso possono realizzare. Poiché i valori assunti da ciascuna variabile sono soltanto 2, con N variabili si avranno 2N possibili combinazioni. Quindi una funzione booleana di N variabili potrà essere rappresentata da una tabella di verità costituita da N+1 colonne e 2N righe. Pertanto l’operatore NOT, che opera su una singola variabile, ha una tabella di verità costituita da 2 righe e 2 colonne, mentre le tabelle di verità relative agli operatori AND e OR, che operano su 2 variabili booleane, sono costituite da 4 righe e 3 colonne. Un’altra considerazione che si può fare è relativa al numero di funzioni che possono essere ottenute a partire da un certo numero di variabili. Poiché ogni riga (corrispondente ad una possibile combinazione delle variabili di ingresso) potrà presentare due possibili valori nella colonna relativa al valore che la funzione assume in corrispondenza di tale combinazione, il numero di combinazioni possibili dei valori di uscita (e quindi di funzioni diverse che si possono generare) sarà 2 elevato al numero delle righe, che abbiamo detto 1 essere 2N, quindi 22N. (Per analogia, nel Totocalcio, in cui si hanno 3 possibili risultati per ognuna delle 13 partite, il numero di possibili combinazioni vincenti è 313). Pertanto, ad esempio, si possono generare 4 diverse funzioni di una sola variabile X, in cui l’uscita: 1. 2. 3. 4. è uno in entrambi i casi (X=0 e X=1) è zero in entrambi i casi è uguale al valore assunto dalla variabile di ingresso (funzione identità) è la negazione della variabile di ingresso (funzione NOT) Nel caso di 2 variabili avremo invece 16 possibili funzioni, fra cui sono particolarmente importanti la funzione XOR (OR esclusivo, il valore dell’uscita è vero se o l’una o l’altra delle variabili di ingresso, ma non entrambe, hanno valore vero) e le funzioni NAND e NOR che sono la negazione di AND e OR, cioè i cui valori sono la negazione dei valori assunti da AND e OR in corrispondenza delle omologhe configurazioni delle variabili di ingresso. X1 X2 X1 XOR X2 0 0 0 1 0 1 0 1 1 1 1 0 X1 X2 X1 NAND X2 X1 X2 X1 NOR X2 0 0 1 0 0 1 0 1 1 0 1 0 1 0 1 1 0 0 1 1 0 1 1 0 Come si può vedere, poiché i valori che le variabili e le funzioni possono assumere sono solo 2, è molto facile che funzioni ottenute mediante combinazioni diverse di operatori forniscano gli stessi risultati. Del resto, è stato detto che tutte le funzioni booleane possono essere espresse utilizzando la coppia di operatori NOT e AND, oppure la coppia NOT e OR. Per verificare che due funzioni booleane siano equivalenti, come ad esempio che X1 NAND X2 equivale a NOT (X1 AND X2), basterà quindi verificare che le loro tabelle di verità siano uguali, che cioè facciano corrispondere lo stesso valore a uguali combinazioni delle variabili su cui sono definite. INTERPRETAZIONE LOGICA DEGLI OPERATORI Come già detto, il primo obiettivo con cui è stata definita l’algebra booleana è quello di rappresentare operazioni logiche in forma di operazioni algebriche. Da questo punto di vista, è possibile identificare le variabili di ingresso delle funzioni booleane come rappresentazioni di fatti o di asserzioni. Ad esempio, se si tratta di figure geometriche e si associa alla variabile X1 l’informazione “ha 3 lati”, alla variabile X2 l’informazione “tutti i lati sono uguali”, alla variabile A1 il concetto “è un triangolo” e alla variabile A2 il concetto “è un triangolo equilatero” si potranno determinare le seguenti relazioni: A1 = X1 A2 = X1 AND X2 2 In questo caso il simbolo = rappresenta la relazione logica di equivalenza, quindi le espressioni precedenti vanno lette come “una figura geometrica è un triangolo se e solo se ha 3 lati” e “una figura geometrica è un triangolo equilatero se e solo se ha tre lati e tutti i lati sono uguali” . In modo analogo espressioni logiche possono esprimere criteri che devono essere soddisfatti, ad esempio quando si ricercano informazioni in una base di dati o con un motore di ricerca su Internet. In quest’ultimo caso, l’interfaccia utente permette di solito (esplicitamente o implicitamente) di esprimere criteri di ricerca sotto forma di funzioni booleane, le cui variabili sono le parole che le pagine Web che si stanno cercando devono contenere: se la parola è presente nella pagina, allora la corrispondente variabile assumerà il valore vero. I siti elencati saranno tutti quelli per cui l’espressione booleana che viene usata come criterio di selezione ha come valore vero. Quindi, se si cercano le pagine che contengono informazioni su cavalli o cani (e non necessariamente su entrambi) si potrà usare come criterio “cavalli OR cani”. Se invece si cercano informazioni sui cavalli, ma si vogliono evitare le pagine in cui si parla di cavalli come unità di misura di potenza dei motori, si potrà adottare il criterio “cavalli AND NOT motore”. Un altro esempio pratico: se cerchiamo una ricetta per una macedonia di frutta e utilizziamo “macedonia” come criterio di ricerca otteniamo anche (e soprattutto) risultati che riguardano la Macedonia intesa come regione balcanica. Occorre quindi restringere la ricerca, utilizzando un criterio più articolato, ad es. “macedonia AND frutta”. 3 OPERATORI BOOLEANI E CALCOLATORI Dal momento che gli operatori booleani possono essere assimilati in ogni caso a funzioni di variabili binarie, non solo vengono inclusi nel set di istruzioni delle CPU, ma tutti i circuiti digitali (reti logiche) che costituiscono l’hardware di un calcolatore sono composti da un elevato numero di circuiti elementari (porte logiche) che realizzano gli operatori logici di base (NOT, AND, OR, NAND, NOR, XOR). Quindi la progettazione di una rete logica, dalle più semplici fino ai microprocessori, che sono sicuramente le più complesse, può essere “ridotta” alla ricerca della funzione logica che produce le uscite desiderate in presenza di certi ingressi. Tuttavia, come si è detto, ci sono molti modi per ottenere la stessa funzione booleana utilizzando diverse combinazioni di operatori logici elementari. Quindi la soluzione ottima per il progetto sarà quella che non solo rispetta le specifiche del progetto (cioè realizza correttamente la funzione desiderata), ma che riesce anche a farlo utilizzando il minor numero di porte logiche. Infatti un circuito più semplice è più economico e anche più veloce nell’eseguire l’operazione per cui è stato progettato. Infatti, da un lato, le onde elettromagnetiche, e quindi i segnali che vengono elaborati all’interno di un calcolatore, si propagano ad una velocità prossima a quella della luce, che è elevatissima ma comunque finita. Pertanto abbreviare il percorso che i segnali devono “attraversare” per compiere una certa operazione comporta una riduzione del tempo di esecuzione. Dall’altro lato, si deve tenere presente che, ogni volta che si attiva una porta logica, i circuiti che la compongono richiedono un tempo significativo (anche se è molto piccolo, tipicamente inferiore a 10-6 / 10-9 secondi, è comunque molto maggiore del tempo di propagazione del segnale lungo un conduttore di lunghezza paragonabile) per raggiungere una configurazione stabile che consente di “leggerne” il risultato correttamente. Per questo motivo, ridurre il numero di porte logiche comporta una riduzione del tempo richiesto per eseguire l’operazione che queste realizzano. A livello di operazioni eseguite dalla CPU, si è visto come i dati su cui queste vengono eseguite siano normalmente raggruppati in byte o in multipli di un byte. Tuttavia abbiamo visto che gli operatori booleani operano su singoli bit. Quindi, come si può interpretare l’istruzione A AND B, dove A e B sono, ad esempio, due parole da 8 bit ? Il risultato dell’applicazione di un operatore booleano a parole di più bit non è altro che una parola della stessa lunghezza in cui ciascun bit rappresenta il risultato dell’operatore applicato ai due bit che si trovano in posizioni corrispondenti nei due operandi. Es. A = 11100110 B = 01011101 A AND B = 01000100 A OR B = 111111111 A B A AND B 11100110 01011101 01000100 (ogni bit è il risultato dell’AND fra i due bit corrispondenti degli operandi) A OR B 11111111 (ogni bit è il risultato dell’OR fra i due bit corrispondenti degli operandi) L’operatore AND può essere utilizzato per “leggere” il contenuto di un singolo bit all’interno di una parola. Dalla tabella di verità dell’operatore AND si può vedere che X AND 0 = 0, qualunque sia il valore di X (infatti, perché l’operatore AND restituisca il valore vero bisogna che entrambi gli operandi abbiano valore vero). Analogamente, X AND 1 = X. Allora, per conoscere il valore del bit più significativo di una parola K, ad es. di 8 bit, si può eseguire l’operazione K AND 10000000, cioè applicare l’AND a K e a una parola composta da tutti zeri eccetto un 1 nella posizione che vogliamo leggere. In questo modo, il risultato per i bit meno significativi sarà sicuramente 0, mentre il risultato sul bit più significativo sarà uguale al contenuto del bit più significativo di K. Quindi basterà verificare se la parola ottenuta ottenuto è o meno uguale a zero (il confronto con lo zero fa sempre parte del set di istruzioni di una CPU): se è 0 allora il bit che abbiamo “letto” ha valore 0, se è diversa da 0 allora il bit è uguale a 1. 4 Es. Determinare il valore del quinto bit (a partire dal meno significativo) della seguente parola: K = 01011000 * Eseguo K AND 00010000 Il risultato è 00010000, diverso da 0, quindi il bit che ho letto è uguale ad 1. Se fosse stato 0 il risultato sarebbe stato 00000000. Agli operatori booleani già visti si aggiungono anche i due operatori di shift (scorrimento) a destra e shift a sinistra. Il risultato di tali operatori è una parola derivata dalla parola cui sono applicati facendo scorrere ogni bit di una posizione a destra o a sinistra. Nel caso dello shift a destra (Shift Right, SHR), il bit più significativo diventa 0 e il bit meno significativo “sparisce”; nel caso dello shift a sinistra (Shift Left, SHL), il bit meno significativo diventa 0 e, se consideriamo una rappresentazione con un numero finito di bit, il bit più significativo “sparisce”, altrimenti si sposta di una posizione a sinistra come tutti gli altri e la parola risultante sarà più lunga di un bit rispetto a quella cui l’operatore è stato applicato. Es. P = 11001001 SHR(P) = 01100100 SHL(P) = 110010010 10010010 (ogni bit si è spostato a destra di una posizione; il MSB è diventato 0). se si hanno a disposizione più di 8 bit per rappresentare il risultato se anche il risultato è rappresentato su 8 bit (ogni bit si è spostato a sinistra di una posizione; il LSB è diventato 0). 5