Queste trasparenze sono disponibili sul sito web dell’autore: http://sgimida.mi.infn.it/~menasce/home.html (selezionare l’opzione COURSES) D.Menasce
Il programma del Corso Prima Parte 1) La rappresentazione interna dei numeri D.Menasce
La differenza fondamentale che esiste fra una macchina calcolatrice ed un computer è il fatto che la prima sa solo fare ciò per cui il suo meccanismo è stato progettato, mentre un computer è in grado di modificare il suo comportamento (e quindi il tipo di calcoli che è richiesto fare) in funzione di un programma che gli viene sottoposto. Nei bei giorni passati, si pensava (erroneamente) che un computer non potesse essere altro che una gigantesca addizionatrice, capace solamente di operazioni aritmetiche. In realtà i computer possono fare molto di più (ciò era vero anche a quei tempi), ma non possono comunque fare tutto. Esistono classi di problemi (i così detti algoritmi non computazionabili) che il modello di von Neumann è inadeguato a trattare. Vediamo ora cosa significa eseguire un programma avendo a disposizione semplicemente una serie di interruttori capaci di essere commutati solamente fra due stati alternativi (ON/OFF o, convenzionalmente, 1/0); questo perchè la tecnologia corrente trova soluzioni praticabili nella realizzazione di un sistema capace di calcolo solamente se alla base la rappresentazione è fra stati binari (a due soli valori). Assumiamo, arbitrariamente che il digit 0 rappresenti lo stato OFF (ad es. un livello nullo di tensione applicata) e che, viceversa, il digit 1 rappresenti lo stato ON (usualmente un livello di 5 V), graficamente: V (Volt) t 1 t V (Volt) 5 D.Menasce
Vediamo come possiamo rappresentare dei numeri (interi) avendo a disposizione un meccanismo capace unicamente di transire fra questi due stati logici: a questo scopo viene naturale utilizzare la rappresentazione binaria (a due valori) dei numeri interi (della realizzazione concreta di un siffatto dispositivo ci occuperemo in seguito): Bit più significativo Bit meno significativo Rappresentazione decimale Rappresentazione binaria Questo nell’ipotesi che si abbiano a disposizione n locazioni per rappresentare un numero... 7 6 5 4 3 2 1 Bit 0 = 0 x 20 0 x 2n + 0 x 2n-1 + … + 0 x 22 + 0 x 21 + = 0 1 = 1 x 20 0 x 21 + 0 x 22 + 0 x 2n + 0 x 2n-1 + … + = 1 1 2 = 0 x 20 1 x 21 + 0 x 22 + 0 x 2n + 0 x 2n-1 + … + = 10 1 3 = 1 x 20 1 x 21 + 0 x 22 + 0 x 2n + 0 x 2n-1 + … + = 11 4 = 0 x 20 0 x 21 + 1 x 22 + 0 x 2n + 0 x 2n-1 + … + = 100 In questo esempio abbiamo deciso arbitrariamente di riservare 8 bit per la rappresentazione di un numero (per questioni storiche un gruppo di 8 bit prende il nome di byte): quale sarà il massimo numero rappresentabile con 8 bit a disposizione? Bit value 255 = = 11111111 1 x 27 + 1 x 26 + 1 x 25 + 1 x 24 + 1 x 23 + 1 x 22 + 1 x 21 + 1 x 20 Il raggruppamento di bit a gruppi di 8 è legato storicamente allo sviluppo delle tabelle dei caratteri: in un computer ogni carattere è rappresentato internamente come insieme di bit. Il primo standard in materia venne definito dalla American Standard Code for Information Interchange (ASCII): in questo standard ogni carattere è rappresentato da 7 bit per un massimo di 128 possibili caratteri rappresentabili. D.Menasce
La tabella dei codici ASCII All’aumentare della velocità di calcolo e delle capacità di memoria dei calcolatori sorse ad un certo punto la necessità di aumentare il numero di caratteri rappresentabili (caratteri speciali e di controllo). L’IBM introdusse un nuovo standard (Extended Binary Coded Decimal Interchenge Code, EBCDIC) in cui il numero di bit usato per rappresentare un carattere fu portato a 8, permettendo la rappresentazione binaria di 256 caratteri, al costo però di un aumento della memoria richiesta per rappresentare un testo proporzionale a 8/7. è così che nel 1959 vide la luce il byte (probabilmente da bocconcino, bite) D.Menasce
La tabella dei codici EBCDIC La tabella dei codici UNICODE (solo i primi 256 caratteri) In tempi recenti, grazie sopratutto alle possibilità introdotte da Internet, lo standard è stato ulteriormente ampliato portando il numero di bit a 16 (consentendo quindi la rappresentazione di 32756 caratteri diversi!!) D.Menasce
La rappresentazione binaria dei numeri interi Il numero di bit utilizzato in un computer per memorizzare un numero varia da 8 (per i primi microprocessori) fino a 128 per i più moderni sistemi (usati nella grafica cinematografica). Il numero finito di bit ha delle implicazioni sottili, oltre a quella ovvia che numeri più grandi di un certo massimo non sono rappresentabili. In particolare è facile vedere che proprietà ben dimostrate in algebra, quali la associatività, vengono gravemente invalidate a causa dell’insieme finito di rappresentabilità numerica del calcolatore. Ricordo che la legge di associatività algebrica è formulata come: (1) a + ( b + c ) = ( a + b ) + c Supponiamo che un ipotetico calcolatore funzioni con una rappresentazione ad una cifra (decimale) con un possibile range [-9,9] e poniamo: a = 7 b = 4 e c = -3 due cifre! sostituendo nella relazione (1): 7 + ( 4 - 3 ) = 8 = 7 + 1 = ( 7 + 4 ) - 3 = 11 - 3 = 8 11 è fuori dalla capacità di rappresentazione del nostro computer, per cui il processore non sarà in grado di terminare il conto ed il programma andrà a termine con un errore di integer overflow Come conseguenza, il calcolo eseguito nel modo indicato a sinistra del simbolo = può essere effettuato nel modo indicato. A destra invece fallisce, per cui la regola di associatività, valida in linea di principio, in pratica non può essere applicata. D.Menasce
La base del sistema numerico (radix) La base di un sistema numerico è data dal numero di simboli fondamentali utilizzati per la rappresentazione di un qualsiasi numero. Nel sistema decimale la base è (ovviamente) 10 in quanto i simboli base sono 1 2 3 4 5 6 7 8 9 e 0. Normalmente i computer lavorano mediante sistemi elettronici bistabili a due soli valori, per cui la base naturale di un computer sarà 2 (0-1). Normalmente le basi più usate sono la binaria, l’ottale, la decimale e l’esadecimale. Base Decimale Ottale 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 La base del sistema ottale è data da solo 8 caratteri 10 caratteri disponibili 10 Di conseguenza il numero 8 deve essere rappresentato riutilizzando almeno due dei caratteri già usati: la prima combinazione possibile è 1 0 11 12 13 14 15 16 17 Il numero successivo sarà indicato con la prima combinazione di simboli già usati, da 0 a 9 (qui 1 e 0) 10 11 12 13 14 15 16 17 20 21 D.Menasce
La base del sistema numerico (radix) La base di un sistema numerico è data dal numero di simboli fondamentali utilizzati per la rappresentazione di un qualsiasi numero. Nel sistema decimale la base è (ovviamente) 10 in quanto i simboli base sono 1 2 3 4 5 6 7 8 9 e 0. Normalmente i computer lavorano mediante sistemi elettronici bistabili a due soli valori, per cui la base naturale di un computer sarà 2 (0-1). Normalmente le basi più usate sono la binaria, l’ottale, la decimale e l’esadecimale. Base Decimale Ottale Binaria 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1 2 3 4 5 6 7 1 La base del sistema binario è data da solo 2 caratteri 10 11 100 Di conseguenza il numero 2 deve essere rappresentato riutilizzando almeno due dei caratteri già usati: la prima combinazione possibile è 1 0 Di nuovo, avendo esaurito tutte le possibili combinazioni a due caratteri, dobbiamo passare a quella a tre, usando i soli simboli disponibili come base, 1 0 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111 10000 10001 10 11 12 13 14 15 16 17 20 21 D.Menasce
La base del sistema numerico (radix) La base di un sistema numerico è data dal numero di simboli fondamentali utilizzati per la rappresentazione di un qualsiasi numero. Nel sistema decimale la base è (ovviamente) 10 in quanto i simboli base sono 1 2 3 4 5 6 7 8 9 e 0. Normalmente i computer lavorano mediante sistemi elettronici bistabili a due soli valori, per cui la base naturale di un computer sarà 2 (0-1). Normalmente le basi più usate sono la binaria, l’ottale, la decimale e l’esadecimale. Base Decimale Ottale Binaria Esadecimale 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1 2 3 4 5 6 7 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111 10000 10001 1 2 3 4 5 6 7 8 9 Questa volta abbiamo a disposizione ben 16 caratteri per la rappresentazione: arbitrariamente essi sono stati codificati dalla seguente lista: 0 1 2 3 4 5 6 7 8 9 A B C D E F Per il numero 10 potremo quindi utilizzare il decimo simbolo disponibile, ossia A 10 11 12 13 14 15 16 17 20 21 A B C D E F Esauriti i 16 simboli si riprende come al solito con la prima accoppiata possibile di due simboli di base, i soliti 1 e 0 10 11 D.Menasce
Conversione da base binaria a base decimale e viceversa Convertire un numero da base binaria a base decimale è relativamente facile: 8 7 6 5 4 3 2 1 0 2 1 1 0 1 1 0 1 0 1 1 = 1x20 + 1x21 + 0x22 + 1x23 + 0x24 + 1x25 + 1x26 + 0x27 + 1x28 = 1 + 2 + 0 + 8 + 0 + 32 + 64 + 0 + 256 = 363 Viceversa, convertire un numero da base decimale a base binaria è un pò più complicato: 1) Si cerca la potenza di 2 più vicina, per difetto, al numero decimale da convertire, in questo caso 8 ( 28 = 256, mentre 29 = 512 > 363) 2) Si mette da parte un 1 per 1x28, si sottrae poi 256 da 363: 363 - 256= 107 3) A questo punto si cerca la potenza di 2 più vicina, sempre per difetto, a 107, ossia 64: poichè da 256 a 64 abbiamo saltato 128 (27 ), metteremo da parte uno 0 per 27 (128) ed un 1 per 26 (64) 4) Proseguendo questo algoritmo iterando i punti 2 e 3 otterremo alla fine 101101011, ossia la rappresentazione binaria di 363 cercata. 1 1 1 D.Menasce
Conversione da base binaria a base ottale Convertire un numero da base binaria a base ottale è invece molto facile: Raggruppiamo i singoli digits a gruppi di 3 1 0 1 1 0 1 0 1 1 Poichè è facile ricordare a memoria la rappresentazione binaria dei primi 8 numeri, si capisce perchè questa rappresentazione (ottale) sia così utilizzata da chi si occupa di programmazione 5 5 3 La rappresentazione decimale di 011 è 3 (poichè consideriamo solo 3 digits alla volta, il massimo numero decimale che potremo rappresentare sarà 7, quindi il massimo numero ottale possibile) Convertire un numero da base binaria a base esadecimale è quasi altrettanto facile: invece di dividere i digits a gruppi di 3, si dividono a gruppi di 4 (poichè la nuova base è 16 occorrono 4 bit per rappresentare 16 possibili caratteri distinti) 1 0 1 1 0 1 0 1 1 1 6 B La rappresentazione decimale di 1011 è 11, che in esadecimale viene rappresentato col carattere B D.Menasce
La somma nel caso di numeri espressi in notazione binaria La somma fra numeri espressi in notazione binaria viene eseguita analogamente a quella fra decimali. Dati due soli simboli, 0 e 1, per rappresentare i numeri, esistono solo quattro possibili somme base che si possono effettuare: 0 + 0 = 0 Torniamo alla nostra somma: 1 1 + 0 = 1 0 + 0 1 + 0 1 0 0 + 1 = 1 1 1 0 + 1 1 + 1 1 + 1 = 10 Carry out, (resto) Sum 10 1 1 0 1 Ma cosa accade se invece di fare una somma vogliamo fare una differenza? In particolare, come si rappresentano i numeri negativi con un calcolatore? E i numeri reali? E quelli complessi? I numeri negativi possono essere rappresentati in quattro diverse notazioni, dette rispettivamente signed magnitude, complemento a uno, complemento a due e rappresentazione in eccesso. Vediamole individualmente. D.Menasce
La rappresentazione binaria dei numeri negativi: signed magnitude Il modo più semplice per rappresentare un numero negativo in forma binaria è quello di riservare il bit più significativo al segno, e di attribuire (arbitrariamente) il simbolo 0 per i numeri positivi ed il simbolo 1 per quelli negativi. Consideriamo una variabile allocata in un byte (8bit): Bit più significativo C’è però un problema: lo zero può essere rappresentato in entrambe le notazioni, quella positiva e quella negativa: (+12)10 = (00001100)2 (0)10 = (00000000)2 byte (0)10 = (10000000)2 (_12)10 = (10001100)2 Dei possibili 28=256 numeri rappresentabili, a causa di questa ambiguità di notazione, solo 28-1=255 saranno effettivamente diversi fra loro. Questa rappresentazione è ovviamente estesa anche al caso di allocazioni a 16, 32 e 64 bit D.Menasce
La rappresentazione binaria dei numeri negativi: complemento a uno Il complemento a uno è un’operazione triviale: basta invertire tutti i bit (gli zeri trasformarli in uno e viceversa). L’operazione di invertire i bit prende il nome di complementazione. (+12)10 = (00001100)2 (_12)10 = ( )2 11110 1 1 Anche questa notazione pone il problema della doppia rappresentazione per lo zero: (0)10 = (00000000)2 (0)10 = (11111111)2 Di nuovo, si hanno solo 255 numeri rappresentabili con 8 bit: questo ed il fatto che la notazione in complemento a uno pone dei problemi nel computo di somme algebriche, ha portato all’utilizzo della notazione in complemento a due (metodo normalmente usato nei calcolatori). D.Menasce
La rappresentazione binaria dei numeri negativi: complemento a due Il complemento a due è un’operazione analoga al complemento a uno: si inverte il valore di tutti i bit e poi si aggiunge uno al numero ottenuto: se rimane un resto, semplicemente lo si scarta. è facile verificare che con questa notazione la doppia rappresentazione dello zero scompare. (+12)10 = (00001100)2 Complemento a uno Se si considera lo zero come un numero positivo (ragionevole visto che il suo bit di segno è zero), sia ha, con 8 bit a disposizione, un uguale numero di cifre poisitve che negative: 11110011 + Complemento a due 1 = 10 i positivi saranno i numeri da 0 a 127 (sono 128 numeri), mentre i negativi saranno da -1 a -128 (sempre 128), 100 1111 100 Se i bit a disposizione sono n, il numero di cifre rappresentabili sarà dato da 2n. I numeri positivi andranno da 0 a 2n-1 e quelli negativi da -1 a -2n (_12)10 = (11110100)2 Il metodo di rappresentazione a complemento a due è correntemente quello utilizzato su praticamente tutti i calcolatori, grazie all’univocità di rappresentazione dello zero e alle proprietà che lo rendono adatto ai calcoli artimetici. Vediamo per ultima la rappresentazione in eccesso. D.Menasce
un valore numerico con il quale traslare il numero desiderato. La rappresentazione binaria dei numeri negativi: rappresentazione in eccesso La rappresentazione in eccesso è utile nel caso della rappresentazione di numeri reali (che vedremo in seguito). In questa rappresentazione, il bit di segno non è contemplato: si procede definendo un bias, un valore numerico con il quale traslare il numero desiderato. Consideriamo di nuovo il numero (+12)10 in una notazione ad 8 bit e scegliamo, arbitrariamente, la notazione in eccesso 128, dove 128 è il bias. La rappresentazione binaria si otterrà nel modo seguente: (128 + (+12) = 140)10 (10001100)2 Il numero negativo (-12)10 si ottiene in modo analogo: (128 + (_12) = 116)10 (01110100)2 Anche in questo caso esiste una sola rappresentazione per lo zero. Il vantaggio di questa rappresentazione consiste nel fatto che i numeri, nel formato bianrio, paiono essere ordinati, da (_128)10 = (00000000)2 fino a (+128)10 = (11111111)2 Questo semplifica gli algoritmi necessari per effettuare confronti fra numeri, e risulta di particolare utilità nella rappresentazione dei numeri reali. D.Menasce
La rappresentazione binaria dei numeri reali Abbiamo esplorato la rappresentazione dei numeri interi: vediamo ora quale sia la rappresentazione più comoda, in formato binario, per i numeri reali. Consideriamo la velocità della luce, c, espressa in Km/s: +2.99792 x 105 Abbiamo qui utilizzato la notazione in virgola mobile ( floating point notation), che permette di rappresentare un vasto insieme di numeri con un ridotto insieme di simboli. Ciò è possibile in quanto questa notazione prevede l’uso di una coppia di numeri, il range e la precisione. Poichè anche i numeri reali hanno segno, occorrerà prevedere spazio per tre quantità nella locazione di bit che dovrà rappresentare il numero, il segno, lo esponente (che data una base, definisce il range) e la mantissa (che, definita la posizione del punto, stabilisce la precisione del numero rappresentato): 2.99792 5 2.99792 x 105 La base si assume implicitamente Precisione Range C’è però un problema: il numero +2.99792 x 105 ed il numero 299.792 x 103 sono indicati in modo diverso ma rappresentano lo stesso numero: questo crea potenziali problemi nella manipolazione e nel confronto fra numeri reali. + 5 2.99792 Segno Esponente Mantissa Per risolvere il problema, si adotta la convenzione di rappresentare i numeri in virgola mobile in una notazione normalizzata. D.Menasce
La notazione normalizzata dei numeri in virgola mobile Per ottenere una rappresentazione univoca di un numero a virgola mobile si adotta la seguente notazione: dato un numero reale, si sposta la virgola fino a portarla a sinistra dell’ultima cifra diversa da zero, e nel fare ciò si riaggiusta corrispondentemente l’esponente per riequilibrare l’ordine di grandezza. Consideriamo il numero +234.2678 x 104 Per portarlo alla rappresentazione normalizzata faremo così: +234.2678 x 104 +23.42678 x 105 è ora giunto il momento di definire quanti bit riservare per le tre componenti di rappresentazione binaria di un numero reale: iniziamo con il definire un semplice schema di base, riservando i seguenti 16 bit: +2.342678 x 106 +.2342678 x 107 Segno Esponente Mantissa in notazione normalizzata Consideriamo ora il numero reale (358)10 e vediamo come fare per rappresentarlo in forma binaria entro lo schema predisposto. D.Menasce
La notazione normalizzata dei numeri in virgola mobile L’esponente è comodo metterlo in forma binaria mediante la rappresentazione in eccesso a 4 (senza approfondire, questo semplifica l’artimetica dell’ addizione): (358)10 Convertiamolo in base esadecimale (166)16 Trasformiamolo da virgola fissa a virgola mobile (da 166 a 166.) (3 + 4 = 7)10 = (00000111)2 (166.)16 x 160 Normalizziamo la notazione (.166)16 x 163 Finalmente riempiamo i campi previsti 7 1 6 6 Segno Esponente Mantissa in notazione normalizzata 1 1 1 1 Il segno è positivo 0111000101100110 D.Menasce
Lo standard IEEE 754 per la rappresentazione dei numeri reali Questo standard indica due formati possibili per la rappresentazione binaria di un numero reale: Single precision (32 bit) 1 8 bits 23 bits Segno Esponente Mantissa Double precision (64 bit) 1 11 bits 52 bits Segno Esponente Mantissa I valori 00000000 e 11111111 per l’esponente hanno significati particolari: 11111111 per esempio è utilizzato per codificare i cosiddetti non numeri (NaN, Not a Number), quali l’infinito, Vediamo le rappresentazioni possibili nell’ambito di questo standard D.Menasce
Lo standard IEEE 754 per la rappresentazione dei numeri reali Valore decimale Segno Esponente Mantissa +1.101 x 25 1000 0100 101 0000 0000 0000 0000 0000 _1.01011 x 2-126 1 0000 0001 010 1100 0000 0000 0000 0000 +1.0 x 2127 1111 1110 000 0000 0000 0000 0000 0000 +0 0000 0000 000 0000 0000 0000 0000 0000 _0 1 0000 0000 000 0000 0000 0000 0000 0000 + 1111 1111 000 0000 0000 0000 0000 0000 +2-128 0000 0000 010 0000 0000 0000 0000 0000 +NaN 1111 1111 011 0111 0000 0000 0000 0000 D.Menasce
Le operazioni artitmetiche in notazione binaria Abbiamo già visto ( ) come si esegue la somma di due numeri in notazione binaria: Consideriamo due numeri positivi a 8 bit, e rappresentiamoli in notazione complemento a due: (+10)10 0 0 0 0 1 0 1 0 + La differenza fra due numeri può essere indicata come la somma di un numero positivo con un numero negativo: è qui che diviene evidente l’eleganza della notazione in complemento a due. (+23)10 0 0 0 1 0 1 1 1 = (+33)10 0 0 1 0 0 0 0 10 0 0 0 10 0 0 10 0 10 1 Carry on bit 5 - 2 = 5 + (-2) (+5)10 0 0 0 0 0 1 0 1 + (+80)10 0 1 0 1 0 0 0 0 + Complemento a due (_2)10 1 1 1 1 1 1 1 0 = (+50)10 0 0 1 1 0 0 1 0 = 10 0 0 0 0 0 10 0 0 0 0 10 0 0 0 10 0 0 10 0 10 1 1 (+3)10 1 0 0 0 10 0 0 10 0 10 1 (_126)10 1 L’ultimo bit di carry on oltre la ottava posizione (8 bit) viene rimosso (discarded) Sbagliato!!! (80+50=130) Abbiamo scelto una notazione a 8 bit, ed il bit più significativo è dedicato al segno, per cui, anche se 10001010 in binario sembra 130, in realtà và interpretato con segno, ossia -126. Occorre a questo punto fare attenzione ad eventuali errori di overflow D.Menasce
legato al software della macchina, bensì allo hardware. Una circostanza per la quale il risultato di un’operazione artimetica genera una condizione di overflow è detta un’exception. In linguaggio corrente si dice che nel programma esiste un baco (un bug) se la sua esecuzione viene interrotta a causa di un errore generato dal programma stesso, ossia dal software. Storicamente però, la prima volta che un calcolatore si è fermato durante l’esecuzione di calcoli, è stato a causa di una falena che aveva messo in corto un interruttore dell’ENIAC, con un errore non legato al software della macchina, bensì allo hardware. La fotografia qui sotto riprodotta rappresenta la pagina del libro di bordo (log-book) degli operatori dell’ENIAC nel giorno in cui si accorsero del problema: è da allora che tradizionalmente un errore software viene detto bug o baco. D.Menasce