RAPPRESENTAZIONE DELL’INFORMAZIONE Internamente a un elaboratore, ogni informazione è rappresentata tramite sequenze di bit (cifre binarie) Una sequenza di bit non dice “cosa” essa rappresenta: l’interpretazione è negli occhi di chi guarda Ad esempio, 01000001 può rappresentare: l’intero 65, il carattere ‘A’, il boolean ‘vero’, … … il valore di un segnale musicale, … il colore di un puntino sullo schermo... INFORMAZIONI NUMERICHE La rappresentazione delle informazioni numeriche è di particolare rilevanza In particolare vogliamo poter trattare: numeri naturali (interi senza segno) numeri interi (con segno) numeri reali con la consapevolezza di: eventuali limiti nella loro rappresentazione e nel loro uso eventuali approssimazioni necessarie. NUMERI NATURALI (interi senza segno) Dominio: N = { 0,1,2,3, …} Rappresentabili con diverse notazioni non posizionali ad esempio la notazione romana: I, II, III, IV, V, .... IX, X, XI... posizionale 1, 2, .. 10, 11, ... 200, ... NUMERI NATURALI (interi senza segno) Non posizionali: hanno regole proprie, spesso assai Dominio: che rendono N = { 0,1,2,3, …} complessa l'esecuzione dei calcoli Rappresentabili con diverse notazioni non posizionali ad esempio la notazione romana: I, II, III, IV, V, .... IX, X, XI... posizionale 1, 2, .. 10, 11, ... 200, ... Posizionale: rappresenta i numeri in modo compatto, e rende semplice l'effettuazione dei calcoli NOTAZIONE POSIZIONALE Concetto di base di rappresentazione B Rappresentazione del numero come sequenza di simboli (cifre) appartenenti a un alfabeto di B simboli distinti ogni simbolo rappresenta un valore compreso fra 0 e B-1 Esempio (base B = tre, alfabeto A = {$,%,#}): $ rappresenta il valore zero % rappresenta il valore uno # rappresenta il valore due NOTAZIONE POSIZIONALE Il valore di un numero espresso in questa notazione è ricavabile a partire dal valore rappresentato da ogni simbolo pesandolo in base alla posizione che occupa nella sequenza dn-1 … d2 d1 d0 Posizione n-1: pesa Bn-1 Posizione 1: pesa B1 Posizione 0: pesa B0 (unità) NOTAZIONE POSIZIONALE Il valore di un numero espresso in questa notazione è ricavabile a partire dal valore rappresentato da ogni simbolo pesandolo in base alla posizione che occupa nella sequenza Esempio ($ indica zero, % indica uno, # indica due): %## rappresenta il valore diciassette %$% rappresenta il valore dieci NOTAZIONE POSIZIONALE Il valore v di un numero espresso in questa notazione è ricavabile a partire dal valore rappresentato da ogni simbolo pesandolo in base alla posizione che occupa nella sequenza In formula: n1 v dk B k k 0 B = base, dk = cifre (ognuna rappresenta un valore fra 0 e B-1) NOTAZIONE POSIZIONALE Quindi, una sequenza di cifre (stringa) non è interpretabile se non si precisa la base in cui è espressa Esempi: Stringa “12” “12” “12” “12” Base quattro otto dieci sedici Alfabeto {0,1,2,3} {0,1,...,7} {0,1,...,9} {0,..,9, A,., F} Calcolo valore 4*1+2 8*1+2 10 * 1 + 2 16 * 1 + 2 Valore sei dieci dodici diciotto NOTAZIONE POSIZIONALE Ogni numero può essere espresso, in modo univoco, in una qualunque base Esempi: Numero venti venti venti venti Base due otto dieci sedici Alfabeto Rappresentazione {0,1} “10100” {0,1,...,7} “24” {0,1,...,9} “20” {0,..,9, A,., F} “14” CONVERSIONI stringa/numero/stringa Ogni numero può essere espresso, in modo univoco, in una qualunque base Quindi, deve essere possibile: data la rappresentazione di un numero in una certa base, determinare il valore del numero dato un numero, determinare la sua rappresentazione in una certa base CONVERSIONI stringa/numero/stringa Ogni numero può essere espresso, in modo univoco, in una qualunque base Quindi, deve essere possibile: Conversione da stringa a numero data la rappresentazione di un numero in una certa base, determinare il valore del numero dato un numero, determinare la sua rappresentazione in una certa base Conversione da numero a stringa CONVERSIONE STRINGA / NUMERO Problema: data la rappresentazione di un numero in una certa base, determinare il valore del numero Soluzione: applicare la formula n1 v dk B k k 0 Stringa “10100” “12” “1A” Base due dieci sedici Alfabeto Calcolo valore Valore {0,1} 1 * 2 4 + 1 * 22 venti {0,1,...,9} 1 * 101 + 2 * 100 dodici {0,..,9, A,., F} 1 * 161 + 10 * 160 ventisei CONVERSIONE NUMERO / STRINGA Problema: dato un numero, determinare la sua rappresentazione Soluzione: dipende se la rappresentazione scelta è posizionale o meno se non è posizionale, le regole dipendono dalla specifica rappresentazione ad esempio, ventisette in notazione romana: 27 è compreso fra 20 e 30 accumulo 20 XX 27-20 = 7 è compreso fra 5 e 10 accumulo 5 V 7-5 = 2 si esprime direttamente II morale: XXVII CONVERSIONE NUMERO / STRINGA Problema: dato un numero, determinare la sua rappresentazione in una base data Soluzione (notazione posizionale): manipolare la formula per dedurre un algoritmo n1 v dk B k v è noto, le cifre dk vanno calcolate k 0 = d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... ))) CONVERSIONE NUMERO / STRINGA n1 v dk B k k 0 = d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... ))) d0 si può ricavare come resto della divisione intera v / B tale divisione ha per quoziente q = d1 + B * ( d2 + B * ( d3 + ... )), che consente di trovare le altre cifre iterando il procedimento NB: le cifre vengono prodotte nell'ordine dalla meno significativa (LSB) alla più significativa (MSB) CONVERSIONE NUMERO / STRINGA n1 v dk B k k0 = d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... ))) Algoritmo delle divisioni successive d0 si può ricavare come resto della divisione intera v / B tale divisione ha per quoziente q = d1 + B * ( d2 + B * ( d3 + ... )), che consente di trovare le altre cifre iterando il procedimento NB: le cifre vengono prodotte nell'ordine dalla meno significativa (LSB) alla più significativa (MSB) CONVERSIONE NUMERO / STRINGA Algoritmo delle divisioni successive si divide v per B il resto costituisce la cifra meno significativa il quoziente serve a iterare il procedimento se tale quoziente è zero, l’algoritmo termina; se non lo è, lo si assume come nuovo valore v’, e si itera il procedimento con il valore v’. CONVERSIONE NUMERO / STRINGA Esempi Numero quattordici undici sessantatre sessantatre Base 4 2 10 16 Calcolo valore 14 / 4 = 3 con resto 2 3 / 4 = 0 con resto 3 11 / 2 = 5 con resto 1 5 / 2 = 2 con resto 1 2 / 2 = 1 con resto 0 1 / 2 = 0 con resto 1 63 / 10 = 6 con resto 3 6 / 10 = 0 con resto 6 63 / 16 = 3 con resto 15 3 / 16 = 0 con resto 3 Stringa “32” “1011” “63” “3F” RAPPRESENTAZIONI IN BASI DIVERSE In generale, le rappresentazioni di uno stesso numero in basi diverse non sono correlate fra loro Esempio: il numero sessantasette B1 = 2 “01000011” B2 = 8 “103” B3 = 10 “67” B4 = 16 “43” RAPPRESENTAZIONI IN BASI DIVERSE Tuttavia, diventano correlate se le basi considerate sono una potenza una dell’altra: B2 = (B1 )N Allora, N cifre nella rappresentazione in base B1 corrispondono esattamente a 1 cifra nella rappresentazione in base B2 RAPPRESENTAZIONI IN BASI DIVERSE Tuttavia, diventano correlate se le basi considerate sono una potenza una dell’altra 8 = 23 Esempio: 1 cifra ottale = 3 cifre binarie B1 = 2 “01000011” B2 = 8 = 23 = (B1)3 “103” 01.000.011 B3 = 10 “67” B4 = 16 = 24 = (B1)4 “43” 0100.0011 16 = 24 1 cifra hex = 4 cifre binarie RAPPRESENTAZIONI IN BASI DIVERSE Conseguenza: se le basi considerate sono una potenza una dell’altra, per passare dalla rappresentazione di un numero in una base B1 alla sua rappresentazione in un’altra base B2 = (B1 )N basta sostituire ordinatamente N cifre della rappresentazione B1 con 1 cifra della rappresentazione B2 RAPPRESENTAZIONI IN BASI DIVERSE Numero uno due tre quattro cinque otto dieci quindici sedici trentuno trentadue cento duecentocinquantacinque Rappr. Base 2 1 10 11 100 101 1000 1010 1111 10000 11111 100000 1100100 11111111 Rappr. Base 8 1 2 3 4 5 10 12 17 20 37 40 144 377 Rappr. Base 16 1 2 3 4 5 8 A F 10 1F 20 64 FF OPERAZIONI IN NOTAZIONE POSIZIONALE Tutte le notazioni posizionali usano le stesse regole per le operazioni, indipendentemente dalla base adottata Esempi di somme e sottrazioni: 15 + 0000 1111 + 0F + 21 = 0001 0101 = 15 = ------------------------36 0010 0100 24 36 - 0010 0100 - 24 21 = 0001 0101 = 15 = -----------------------15 0000 1111 0F OPERAZIONI IN NOTAZIONE POSIZIONALE In moltiplicazioni e divisioni: spostando tutte le cifre a sinistra di una posizione (e introducendo uno 0 a destra) si moltiplica per la base spostando tutte le cifre a destra di una posizione (e introducendo uno 0 a sinistra) si divide per la base Esempi: base dieci: base dieci: base due: base due: 184 * 1832 / 1011 * 1111 / 10 10 2 2 = 1840 = 183 _ = 10110 = 111 _ Divisione intera OPERAZIONI IN NOTAZIONE POSIZIONALE Due mosse elementari: Shift Left (SHL) 15 * 4 = 0000 1111 SHL 2 = ----------------------------60 0011 1100 Shift Right (SHR) 25 / 8 = 0001 1001 SHR 3 = ----------------------------3 0000 0011 Qualunque moltiplicazione o divisione può essere espressa con somme, sottrazioni e SHL / SHR. ERRORI NELLE OPERAZIONI In matematica, le operazioni sui naturali non danno mai luogo a errori (posto che la divisione è una divisione intera, che può comportare l’esistenza di un resto) In un elaboratore, invece, si possono generare errori, a causa dell’impossibilità di rappresentare tutti gli infiniti numeri in particolare, con N bit il massimo numero rappresentabile è 2N-1 qualunque operazione che implichi un risultato maggiore sarà errata OVERFLOW ERRORI NELLE OPERAZIONI Teoricamente decimale 176 + 84 = ---260 binario 1011 0000+ 0101 0100= ---------1 0000 0100 Praticamente decimale 176 + 84 = ---004 binario 1011 0000+ 0101 0100= ---------0000 0100 il risultato è completamente errato, perché è andato perso proprio il contributo più significativo (MSB) Soluzione: usare un tipo di dato che offra un maggior numero di bit IMPLEMENTARE GLI ALGORITMI Conversione da stringa a numero si applica la formula n1 v dk B k 0 k le cifre dk sono note, il valore v va calcolato = d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... ))) richiede la valutazione di un polinomio Metodo di Horner IMPLEMENTARE GLI ALGORITMI Conversione da stringa a numero una funzione: sToNum() in ingresso: base b stringa di simboli, s lunghezza della stringa, len in uscita: il valore del numero (intero senza segno) IMPLEMENTARE GLI ALGORITMI unsigned long sToNum( unsigned short b, char s[], int len) { <finché ci sono simboli in s> <calcola il valore del simbolo s[i]> <aggiorna il valore v> <alla fine, v rappresenta il valore cercato> } IMPLEMENTARE GLI ALGORITMI unsigned long sToNum( unsigned short b, char s[], int len) { unsigned long v = 0; <finché ci sono simboli in s> <calcola il valore del simbolo s[i]> <aggiorna il valore v> <alla fine, v rappresenta il valore cercato> } IMPLEMENTARE GLI ALGORITMI unsigned long sToNum( unsigned short b, char s[], int len) { unsigned long v = 0; int i = 0; for(i=0; i<len; i++) { <calcola il valore del simbolo s[i]> <aggiorna il valore v> } <alla fine, v rappresenta il valore cercato> } IMPLEMENTARE GLI ALGORITMI unsigned long sToNum( unsigned short b, char s[], int len) { unsigned long v = 0; int i = 0; for(i=0; i<len; i++) { val = valoreCifra(s[i]); v = b * v + val; /* Horner */ } <alla fine, v rappresenta il valore cercato> } IMPLEMENTARE GLI ALGORITMI unsigned long sToNum( unsigned short b, char s[], int len) { unsigned long v = 0; int i = 0; for(i=0; i<len; i++) { v = b * v + valoreCifra(s[i]);; } return v; valoreCifra(), } chi era costei ? IMPLEMENTARE GLI ALGORITMI unsigned valoreCifra(char ch) { come fare per calcolare il valore? } il carattere è rappresentato internamente da un numero, secondo la codifica ASCII è garantito che i caratteri da ‘0’ a ‘9’ sono in sequenza: quindi, se ‘0’ è rappresentato internamente dal numero ‘1’ deve essere rappresentato dal numero +1, ‘2’ deve essere rappresentato dal numero +2, … ‘9’ deve essere rappresentato dal numero +9 IMPLEMENTARE GLI ALGORITMI unsigned valoreCifra(char ch) { come fare per calcolare il valore? } conseguenza: la differenza tra un carattere numerico (compreso fra ‘0’ e ‘9’) e il carattere ‘0’ è proprio il valore del simbolo stesso! ‘0’ -’0’ = - = 0 ‘1’ -’0’ = (+1) - = 1 ‘2’ -’0’ = (+2) - = 2 … ‘9’ -’0’ = (+9) - = 9 IMPLEMENTARE GLI ALGORITMI unsigned valoreCifra(char ch) { come fare per calcolare il valore? } lo stesso approccio vale per le lettere da ‘A’ a ‘F’ che rappresentano i valori da 10 a 15 nel caso della base sedici (esadecimale); quindi, se ‘A’ è rappresentato internamente dal numero ‘B’ deve essere rappresentato dal numero +1, ‘C’ deve essere rappresentato dal numero +2, … ‘Z’ deve essere rappresentato dal numero +25 IMPLEMENTARE GLI ALGORITMI unsigned valoreCifra(char ch) { come fare per calcolare il valore? } e anche per le minuscole da ‘a’ a ‘f’, che pure rappresentano i valori da 10 a 15: se ‘a’ è rappresentato internamente dal numero ‘b’ deve essere rappresentato dal numero +1, ‘c’ deve essere rappresentato dal numero +2, … ‘z’ deve essere rappresentato dal numero +25 IMPLEMENTARE GLI ALGORITMI unsigned valoreCifra(char ch) { come fare per calcolare il valore? } conseguenza: la differenza tra un carattere alfabetico compreso fra ‘A’ e ‘F’ (o fra ‘a’ e ‘f’) e il carattere ‘A’ (oppure, rispettivamente, ‘a’) consente di trovare il valore corrispondente ‘A’ -’A’ = - = 0 sommando 10 ottengo 10 ‘B’ -’A’ = ( +1) - = 1 sommando 10 ottengo 11 ‘C’ -’A’ = ( +2) - = 2 sommando 10 ottengo 12 … ‘F’ -’A’ = ( +5) - = 5 sommando 10 ottengo 15 IMPLEMENTARE GLI ALGORITMI unsigned valoreCifra(char return ('0'<= ch)&&(ch <= '9') ch - '0' : ('a'<= ch)&&(ch <= 'f') ch - 'a' + 10 : ('A'<= ch)&&(ch <= 'F') ch - 'A' + 10 : BOH; } ch) { ? ? ? Qui la funzione è indeterminata (questo caso non dovrebbe mai verificarsi) IMPLEMENTARE GLI ALGORITMI un approccio ricorsivo unsigned long sToNum( unsigned short b, char s[], int len) { <se len=0, il valore è 0> <se len=1, il valore è val(s[0])> <in ogni altro caso, il valore è val(s[len-1]) + b * sToNum(b,s,len-1) > } IMPLEMENTARE GLI ALGORITMI un approccio ricorsivo unsigned long sToNum( unsigned short b, char s[], int len) { if (len==0) return 0; else if (len==1) return valoreCifra(s[0]); else return valoreCifra(s[len-1]) + b * sToNum(b,s,len-1) ; } IMPLEMENTARE GLI ALGORITMI Esempio: un cliente main(){ char st[] = "367"; unsigned long val8 = sToNum(8, st,3); unsigned long val10 = sToNum(10,st,3); unsigned long val16 = sToNum(16,st,3); /* quanto valgono le variabili? */ } Provare con entrambe le versioni di sToNum() CONVERSIONE NUMERO / STRINGA Problema: dato un numero, determinare la sua rappresentazione in una base data Soluzione (notazione posizionale): manipolare la formula per dedurre un algoritmo n1 v dk B k v è noto, le cifre dk vanno calcolate k 0 = d0 + B * ( d1 + B * ( d2 + B * ( d3 + ... ))) IMPLEMENTARE GLI ALGORITMI Algoritmo delle divisioni successive si divide v per B il resto costituisce la cifra meno significativa il quoziente serve a iterare il procedimento se tale quoziente è zero, l’algoritmo termina; se non lo è, lo si assume come nuovo valore v’, e si itera il procedimento con il valore v’. IMPLEMENTARE GLI ALGORITMI Conversione da numero a stringa una funzione: numToS() in ingresso: base b numero n in uscita: la passiamo come parametro (passa per riferimento) ipotesi: inizialmente è vuota ed è sufficientemente lunga stringa di simboli, s (lunghezza della stringa) implicita nella stringa IMPLEMENTARE GLI ALGORITMI void numToS(unsigned short b, unsigned long v, char s[]) { <ripeti> <calcola il resto v%B> <calcola il carattere corrispondente e inseriscilo in testa alla stringa> <sostituisci a v il nuovo valore v/B > <per tutto il tempo che v>0 > } IMPLEMENTARE GLI ALGORITMI void numToS(unsigned short b, unsigned long v,scrivere charuna s[]) { occorre funzione char convertiCifra(unsigned <ripeti> int) <calcola il resto v%B> <calcola il carattere corrispondente> <inseriscilo in testa alla stringa> <sostituisci a v il nuovo valore v/B > <per tutto il tempooccorre che v>0 > una procedura scrivere } void aggiungiInTesta(...) che lo faccia IMPLEMENTARE GLI ALGORITMI void numToS(unsigned short b, unsigned long v, char s[]) { do { <calcola il resto v%B> <calcola il carattere corrispondente e inseriscilo in testa alla stringa> <sostituisci a v il nuovo valore v/B > } while (v>0); } IMPLEMENTARE GLI ALGORITMI void numToS(unsigned short b, unsigned long v, char s[]) { do { resto = v % b; <calcola il carattere corrispondente e inseriscilo in testa alla stringa> <sostituisci a v il nuovo valore v/B > } while (v>0); } IMPLEMENTARE GLI ALGORITMI void numToS(unsigned short b, unsigned long v, char s[]) { do { resto = v % b; ch = convertiCifra(resto); aggiungiInTesta(ch,s); <sostituisci a v il nuovo valore v/B > } while (v>0); } IMPLEMENTARE GLI ALGORITMI void numToS(unsigned short b, unsigned long v, char s[]) { do { resto = v % b; ch = convertiCifra(resto); aggiungiInTesta(ch,s); v = v / b; } while (v>0); } IMPLEMENTARE GLI ALGORITMI void numToS(unsigned short b, unsigned long v, char s[]) { do { ch = convertiCifra(v % b); aggiungiInTesta(ch,s); v = v / b; } while (v>0); } IMPLEMENTARE GLI ALGORITMI void numToS(unsigned short b, unsigned long v, char s[]) { do { aggiungiInTesta( convertiCifra(v % b), s); v = v / b; } while (v>0); } IMPLEMENTARE GLI ALGORITMI char convertiCifra(unsigned n) { return ( 0 <= n)&&(n <= 9) ? n + '0' : (10 <= n)&&(n <= 15) ? n - 10 + 'A' : '_'; } Qui la funzione è indeterminata (questo caso non dovrebbe mai verificarsi) IMPLEMENTARE GLI ALGORITMI void aggiungiInTesta( char ch, char st[]) { <sposta tutti i caratteri a destra di una posizione, per fare posto al nuovo carattere> <copia il nuovo carattere nella prima posizione della stringa> } IMPLEMENTARE GLI ALGORITMI void aggiungiInTesta( char ch, char st[]) { int i; for(i=strlen(st); i>=0; i--){ st[i+1] = st[i]; } <copia il nuovo carattere nella prima posizione della stringa> } IMPLEMENTARE GLI ALGORITMI void aggiungiInTesta( char ch, char st[]) { int i; for(i=strlen(st); i>=0; i--) st[i+1] = st[i]; st[0] = ch; } IMPLEMENTARE GLI ALGORITMI Esempio: un cliente main(){ char s2[10] = "", s8[5] = "", s10[5] = "", s16[5] = ""; numToS( 2, 250, s2); numToS( 8, 250, s8); numToS(10, 250, s10); numToS(16, 250, s16); /* quanto valgono le stringhe? */ } IMPLEMENTARE GLI ALGORITMI un approccio ricorsivo void numToS(unsigned short b, unsigned long v, char s[]) { <calcola il carattere corrispondente al resto v%B e inseriscilo in coda alla stringa> <se v=0, ritorna> [altrimenti] <calcola la stringa corrisp. a v/B> } È la ricorsione che “inverte l’ordine” NUMERI INTERI (con segno) Dominio: Z = { …, -2,-1,0,1,2,3, … } Rappresentare gli interi in un elaboratore pone alcune problematiche: come rappresentare il “segno meno”? possibilmente, rendere semplice l’esecuzione delle operazioni magari usando gli stessi circuiti usati per i naturali…? NUMERI INTERI (con segno) Due possibilità: rappresentazione in modulo e segno semplice e intuitiva… … ma inefficiente e complessa nella gestione delle operazioni non molto usata in pratica rappresentazione in complemento a due meno intuitiva, costruita “ad hoc” ma efficiente e capace di rendere semplice la gestione delle operazioni largamente usata NUMERI INTERI (con segno) Rappresentazione in modulo e segno un bit per rappresentare il segno 0 = + 1 = - N-1 bit per rappresentare il valore assoluto Esempi (su 8 bit, MSB rappresenta il segno): + 5 = 0 0000101 - 36 = 1 0100100 NUMERI INTERI (con segno) Rappresentazione in modulo e segno Difetti: due diverse rappresentazioni per lo zero + 0 = 00000000 - 0 = 10000000 occorrono algoritmi speciali per fare le operazioni se si adottano le usuali regole, non è verificata la proprietà X + (-X) = 0 occorrono regole (e quindi circuiti) ad hoc NUMERI INTERI (con segno) +5 Rappresentazione in modulo -5 --Difetti: 0 0 0000101 e segno 1 0000101 ---------1 0001010 due diverse rappresentazioni per lo zero + 0 = 00000000 Cos’è - 0 =questa 10000000 roba??? (+5) + (-5) = -10 ??? occorrono algoritmi speciali per fare le operazioni se si adottano le usuali regole, non è verificata la proprietà X + (-X) = 0 occorrono regole (e quindi circuiti) ad hoc NUMERI INTERI (con segno) Rappresentazione in complemento a due si vogliono poter usare le regole standard per fare le operazioni in particolare, si vuole che X + (-X) = 0 la rappresentazione dello zero sia unica anche a prezzo di una notazione più complessa, meno intuitiva, e magari non (completamente) posizionale ! NUMERI INTERI (con segno) Rappresentazione in complemento a due idea: cambiare il peso del bit più significativo da +2N-1 a -2N-1 il peso degli altri bit rimane intoccato. Esempi (su 8 bit, MSB ha peso negativo): 0 0000101 = +5 1 0000101 = -128 + 5 = - 123 1 1111101 = -128 + 125 = - 3 NUMERI INTERI (con segno) Rappresentazione in complemento a due MSB=0 numero o nullo idea: cambiare il peso del bitpositivo più MSB=1 numero negativo N-1 N-1 significativo da +2 a -2 Ma nel secondo caso gli altri bit il valore intoccato. assoluto! il peso degli non altrisono bit rimane Esempi (su 8 bit, MSB ha peso negativo): 0 0000101 = +5 1 0000101 = -128 + 5 = - 123 1 1111101 = -128 + 125 = - 3 NUMERI INTERI (con segno) Rappresentazione in complemento a due Intervallo di numeri rappresentabili se MSB=0, è come per i naturali con N-1 bit da 0 a 2N-1-1 Esempio: su 8 bit, [0,+127] se MSB=1, stesso intervallo traslato di -2N-1 da -2N-1 a -1 Esempio: su 8 bit, [-128,-1] Intervallo globale: [-2N-1 , -2N-1-1] su 8 bit, [-128,+127] su 16 bit, [-32768,+32767] NUMERI INTERI (con segno) Rappresentazione complemento a due Lo stessoinintervallo N-1] prima era tutto sui positivi [0...2 Intervallo di numeri rappresentabili ora è metà sui positivi e metà sui se MSB=0,negativi è come[- per i naturali 2N-1 ... 2N-1-1] con N-1 bit fra i positivi da 0 a 2N-1lo-1zero rientra Esempio: su 8 bit, [0,+127] se MSB=1, stesso intervallo traslato di -2N-1 da -2N-1 a -1 Esempio: su 8 bit, [-128,-1] Intervallo globale: [-2N-1 , -2N-1-1] su 8 bit, [-128,+127] su 16 bit, [-32768,+32767] CONVERSIONE NUMERO / STRINGA Osservazione: poiché si opera su N bit, questa è in realtà una aritmetica mod 2N La rappresentazione del numero v coincide con quella del numero v + 2N Conseguenza: possiamo in realtà calcolare la rappresentazione di v’ = v + 2N v dn1 B n 2 n1 dk B v' dn1B k k0 n1 È un naturale! n 2 dk B k k0 CONVERSIONE NUMERO / STRINGA Per calcolare la rappresentazione di v (v<0) si può calcolare quella di v’ = v + 2N Esempio (8 bit, 2N = 256): per calcolare la rappresentazione di -3 calcoliamo quella del naturale 253 -3 = -128 + 125 “11111101” 253 “11111101” CONVERSIONE NUMERO / STRINGA Problema: dato un numero negativo v, come determinare praticamente la sua rappresentazione in notazione complemento a due? Poiché sappiamo convertire un naturale in stringa binaria, il problema diventa: Partendo dalla rappresentazione binaria del valore assoluto |v|, come giungere alla rappresentazione in complemento a due del valore opposto -|v| ? CONVERSIONE NUMERO / STRINGA Partendo dalla rappresentazione binaria del valore assoluto |v|, come giungere alla rappresentazione in complemento a due del valore opposto -|v| ? • Poiché v = - |v| v’ = v + 2N = 2N - |v| • tutto sta a riuscire a calcolare 2N - |v|... • ...che però richiede una sottrazione! CONVERSIONE NUMERO / STRINGA Fortunatamente, 2N - |v| è una sottrazione solo in apparenza, in quanto: 2N - |v| = (2N -1)- |v| + 1 Poiché (2N -1) è una sequenza di N “uni”, calcolare (2N -1)- |v| equivale a invertire tutti i bit della stringa che rappresenta |v|: v = -3 |v| “00000011” (2N -1)- |v| “11111100” alla fine basta quindi aggiungere 1. CONVERSIONE NUMERO / STRINGA Algoritmo di complementazione a due Data una stringa di bit che rappresenta il valore v, per ottenere la stringa che rappresenta il valore opposto -v occorre: • invertire tutti i bit della stringa data • aggiungere 1 al risultato così ottenuto. Esempi v = -3 (3 “00000011” ) “11111101” v = -37 (37 “00100101” ) “11011011” “11111101” “00000011” |v| = 3 v = -3 CONVERSIONE STRINGA / NUMERO Problema: data la rappresentazione di un numero intero in notazione complemento a due, determinare il valore del numero Soluzione: applicare la formula v dn1 B n 2 n1 dk B k k0 oppure: applicare l’algoritmo di complementazione e sfruttare la formula dei naturali per dedurre il valore assoluto del numero. OPERAZIONI SU NUMERI INTERI Rappresentazione in complemento a due Questa rappresentazione rende possibile fare addizioni e sottrazioni con le usuali regole algebriche Un primo esempio: -5 + +3 = ---2 11111011 00000011 -------11111110 Funziona! OPERAZIONI SU NUMERI INTERI Rappresentazione in complemento a due In certi casi occorre però una piccola convenzione: ignorare il riporto Un altro esempio: -1 + -5 = ---6 11111111 11111011 -------(1)11111010 Funziona… purché si ignori il riporto! OPERAZIONI SU NUMERI INTERI Rappresentazione in complemento a due Nelle sottrazioni, analogamente, può capitare di dover ignorare il prestito +3 +5 = --2 (1)00000011 00000101 = -------11111110 +3 -5 = -+8 (1)00000011 11111011 = -------00001000 Ma.. perché ignorando prestiti e riporti funziona?? OPERAZIONI: PERCHÉ FUNZIONANO Il motivo è semplice: poiché si opera su N bit, questa è in realtà una aritmetica modulare, di modulo 2N ignorando riporti (o inserendo prestiti) si introduce un errore pari a 2N che, quindi, mod 2N scompare! Nota: possono però prodursi errori se viene invaso il bit più significativo ERRORI NELLE OPERAZIONI In un elaboratore che opera in notazione complemento a due, si ha errore se si supera il massimo intero (positivo o negativo) rappresentabile, cioè se si crea un riporto dal penultimo all’ultimo bit Esempio: 60 + 75 = ----135 00111100 01100011 -------10011111 Errore! Si è invaso il bit di segno, il risultato è negativo! (Può capitare solo sommando due positivi o due negativi) SHIFT CON INTERI IN COMPLEMENTO A 2 La semantica delle operazioni di shift Shift Left (SHL) = moltiplicare per 2 Shift Right (SHR) = dividere per 2 è mantenuta in complemento a due? Sì, purché lo Shift Right (SHR) tenga conto del segno, ossia introduca uno 0 da sinistra, se MSB=0 introduca un 1 da sinistra, se MSB=1 Questo shift si chiama Shift Aritmetico SHIFT CON INTERI IN COMPLEMENTO A 2 Esempio di shift a sinistra: -10 * 4 = -10 SHL 2 11110110 SHL 2 = 11011000 -40 Esempio di shift (aritmetico) a destra: -10 / 4 = -10 SHR 2 11110110 SHR 2 = 11111101 -3 Attenzione: lo Shift Right ha la semantica della divisione intera il quoziente è il massimo intero minore o uguale a X/Y: non è un semplice troncamento! IMPLEMENTARE GLI ALGORITMI Conversione da stringa a numero o si applica direttamente la formula v dn1 B n 2 n1 oppure dk B k k0 se MSB=0 (positivo) si usa sToNum() se MSB=1 (negativo), si sfrutta la relazione v = v’ - 2N, usando sToNum() per ottenere v’, e sottraendo 2N dal risultato. funzione sToInt() IMPLEMENTARE GLI ALGORITMI Conversione da numero a stringa se il numero è positivo si applica l’algoritmo delle divisioni successive (si ottiene MSB=0) se invece il numero è negativo si applica l’algoritmo delle divisioni successive al numero v’ = v + 2N (ciò assicura MSB=1) funzione intToS() NUMERI REALI Dominio: R Un soprainsieme degli interi alcune proprietà degli interi potrebbero non essere più verificate Un numero reale può non essere finitamente rappresentabile come stringa di simboli in nessuna base: numeri irrazionali (, e, ...) in alcune basi: numeri razionali periodici NUMERI REALI La rappresentazione di un numero razionale può risultare periodica o meno, a seconda della base adottata In particolare, non è mai periodica se si assume come base il denominatore della sua forma fratta 1/3 = (0.333333333333...)10 = (0.1)3 8/7 = (1.142857142857...)10 = (1.1)7 ... NUMERI REALI IN DIVERSE BASI Se la rappresentazione di un numero razionale in base B è periodica, allora è periodica anche la rappresentazione dello stesso numero in base B’ = B/k il viceversa vale solo se B’ = Bn Quindi un numero periodico in base 10 è sicuramente: periodico anche in base 2 (perché 10 = 2*5) un numero periodico in base 2 può essere o non essere periodico in base 10… … ma lo è certamente in base 4, 8, 16, ... NUMERI REALI IN DIVERSE BASI Se la rappresentazione di un numero razionale in base B è periodica, allora è periodica anche la rappresentazione dello stesso numero in base B’ = B/k il viceversa vale solo se B’ = Bn se non bastavano i fattori primi Intuitivamente: Quindi della base B a esprimere il numero in forma finita, un numero periodico in base 10 è sicuramente: la situazione non può certo migliorare avendo meno periodico anche in base 2 (perché 10 = 2*5) fattori a disposizione (B’ = B / k), mentre potrebbe un numero periodico in base 2 può essere o migliorare avendo a disposizione nuovi fattori periodico in base 10… (B” non = r * essere B) … ma lo è certamente in base 4, 8, 16, ... NUMERI REALI IN DIVERSE BASI Ovviamente, se B’ = Bn, i fattori primi disponibili gli stessi (cambia Serimangono la rappresentazione disolo un l’esponente numero a cui compaiono) e quindi la situazione non può razionale in base B è periodica, allora cambiare: se era periodico rimane periodico, se era è non periodica rappresentazione periodicoanche rimanela non periodico. dello stesso numero in base B’ = B/k il viceversa vale solo se B’ = Bn Quindi un numero periodico in base 10 è sicuramente: periodico anche in base 2 (perché 10 = 2*5) un numero periodico in base 2 può essere o non essere periodico in base 10… … ma lo è certamente in base 4, 8, 16, ... NUMERI REALI: MANTISSA E RESTO Dato un numero reale V, e fissati: una base B un naturale N è sempre possibile esprimere V come somma di due contributi, di cui il primo costituito da esattamente N cifre: V m * Besp + r * Besp-N mantissa (n cifre) esponente (intero) resto (reale) NUMERI REALI: MANTISSA E RESTO Esistono infinite triple m, esp, r che consentono di esprimere, a parità di B e N, lo stesso numero reale V. Ad esempio, se V=31.4357, N=4, B=10: 314,3 31,43 3,143 ,3143 ,0314 ,0031 ,0003 ,0000 Scomposizione * 10-1 + 570, * 100 + 57, * 101 + 5,7 2 * 10 + ,57 3 * 10 + ,357 4 * 10 + ,4357 * 105 + ,14357 * 106 + ,314357 * * * * * * * * -1-4 10 100-4 101-4 102-4 103-4 104-4 105-4 106-4 m m=314.3 m=31.43 m=3.143 m=.3143 m=.0314 m=.0031 m=.0003 m=.0000 r r=570 r=57 r=5.7 r=.57 r=.357 r=.4357 r=.14357 r=.314357 esp esp=-1 esp =0 esp =1 esp =2 esp =3 esp =4 esp =5 esp =6 RAPPRESENTAZIONE NORMALIZZATA Poiché la rappresentazione deve essere unica, occorre fare una scelta Si sceglie la tripla m, esp, r tale che 1/B m < 1, r<1 Rappresentazione normalizzata Scomposizione ..... 1 3,143 * 10 + 5,7 ,3143 * 102 + ,57 ,0314 * 103 + ,357 ..... * 101-4 * 102-4 * 103-4 m .... m=3.143 m=.3143 m=.0314 .... r .... r=5.7 r=.57 r=.357 .... esp .... esp =1 esp =2 esp =3 .... RAPPRESENTAZIONE NORMALIZZATA In pratica, è quelladeve in cui essere la man Poiché la rappresentazione tissa è <1, e la sua prima cifra unica, occorre fare una scelta dopo la virgola è diversa da 0 Si sceglie la tripla m, esp, r tale che 1/B m < 1, r<1 Rappresentazione normalizzata Scomposizione ..... 1 3,143 * 10 + 5,7 ,3143 * 102 + ,57 ,0314 * 103 + ,357 ..... * 101-4 * 102-4 * 103-4 m .... m=3.143 m=.3143 m=.0314 .... r .... r=5.7 r=.57 r=.357 .... esp .... esp =1 esp =2 esp =3 .... NUMERI REALI: IL VINCOLO Un numero reale ha spesso una rappresentazione infinita in una data base, ma rappresentare infinite cifre è impossibile Ergo, assumiamo come rappresentazione approssimata del numero reale V il solo contributo m * Besp V m * Besp Il resto si trascura Errore di troncamento NUMERI REALI: LE SCELTE OPERATIVE In pratica dobbiamo stabilire: Quante cifre binarie (bit) per la mantissa? Quante cifre per l’esponente? Espresso come? Come rappresentare il segno del numero? Osservazione: nel caso B=2, la mantissa normalizzata è compresa fra 1/2 e 1: 1/2 m < 1 il primo bit dopo la virgola è sempre 1. NUMERI REALI: LE SCELTE OPERATIVE In pratica dobbiamo stabilire: Quante cifre binarie (bit) per la mantissa? Quante cifre per l’esponente? Espresso come? Ma allora… si può evitare di scriverlo esplicitamente! In effetti, un Come rappresentare il bit segno del numero? prefissato non porta informazione!! Osservazione: nel caso B=2, la mantissa normalizzata è compresa fra 1/2 e 1: 1/2 m < 1 il primo bit dopo la virgola è sempre 1. NUMERI REALI: LE SPECIFICHE DEL C Float (IEEE-32; 4 byte) 1 bit per il segno del numero (0 = +, 1 = -) 8 bit per l’esponente esp, codificato con eccesso 126 (28-1-2) valori da 127 a 254 esponenti positivi [1..128] valori da 1 a 125 esponenti negativi [-125..-1] i valori estremi (0 e 255) sono riservati 23 bit per la mantissa m (cioè n=24 bit effettivi, contando l’MSB non rappresentato) dal byte meno significativo al più significativo NUMERI REALI: LE SPECIFICHE DEL C Double (IEEE-64; 8 byte) 1 bit per il segno del numero (0 = +, 1 = -) 11 bit per l’esponente esp, codificato con eccesso 1022 (211-1-2) valori da 1023 a 2046 esp. positivi [1..1024] valori da 1 a 1021 esp. negativi [-1021..-1] i valori estremi (0 e 2047) sono riservati 52 bit per la mantissa m (cioè n=53 bit effettivi, contando l’MSB non rappresentato) dal byte meno significativo al più significativo NUMERI REALI: LE SPECIFICHE DEL C Casi particolari: float esp=0, m=0 rappresentano 0.0 esp=255, m=0 rappresentano esp=255, m0 rappresentano un errore double esp=0, m=0 rappresentano 0.0 esp=2047, m=0 rappresentano esp=2047, m0 rappresentano un errore NUMERI REALI: LE SPECIFICHE DEL C Valori rappresentabili (lato positivo): float [ .1 * 21-126 ... .1111..111*2254-126 ] [ 2-126 ... 2128 ] cioè [ 1.2 * 10-38 … 3.4 * 1038 ] double [ .1 * 21-1022 ... .1111..111*22046-1022 ] [ 2-1022 ... 21024 ] cioè [ 1.3 * 10-308 … 0.7 * 10308 ] NUMERI REALI: CIFRE SIGNIFICATIVE Cifre significative Poiché assumendo V m * Besp trascuriamo il resto r * Besp-N, e poiché nella forma nornalizzata r<1, l’errore vale: Eassoluto Besp-N Esso non è molto significativo in sé: lo è di più se rapportato al valore del numero Erelativo Besp-N / (m * Besp) da cui, poiché 1/B m < 1, Erelativo Besp-N / Besp-1 = B1-N NUMERI REALI: CIFRE SIGNIFICATIVE Cifre significative Se dunque Erelativo B1-N , le cifre decimali significative risultano: float N=24 Er 2-23 = 10-23*log2 = 10-7 circa 7 cifre decimali significative double N=53 Er 2-52 = 10-52*log2 = 10-15 circa 15 cifre decimali significative NUMERI REALI: CIFRE SIGNIFICATIVE Cifre significative Epsilon di macchina: il più float chedecimali la macchina Se dunque Erelativo piccolo B1-N , le cifre distingue come diverso da 0 significative risultano: float N=24 Er 2-23 = 10-23*log2 = 10-7 circa 7 cifre decimali significative double N=53 Er 2-52 = 10-52*log2 = 10-15 circa 15 cifre decimali significative il più piccolo double distinguibile da 0 NUMERI REALI: ESEMPIO 1 Rappresentazione come float di V = 1.0 rappr. normalizzata: V = 1.010 = 0.12 * 21 segno (1 bit): 0 mantissa (24 bit): .10000000 00000000 00000000 esponente (8 bit con eccesso 126) esp=1 126+1 = 127 01111111 segno 0 esponente 0111 1111 mantissa normalizzata (23 bit, MSB escluso) 000 0000 0000 0000 0000 0000 in memoria: byte 1 0011 1111 byte 2 1000 0000 byte 3 0000 0000 byte 4 0000 0000 NUMERI REALI: ESEMPIO 2 Rappresentazione come float di V = 5.875 rappr. normalizzata: V = 101.1112 = .1011112 * 23 segno (1 bit): 0 mantissa (24 bit): .10111100 00000000 00000000 esponente (8 bit con eccesso 126) esp=3 126+3 = 129 10000001 segno 0 esponente 1000 0001 mantissa normalizzata (23 bit, MSB escluso) 011 1100 0000 0000 0000 0000 in memoria: byte 1 01000000 byte 2 1011 1100 byte 3 0000 0000 byte 4 0000 0000 NUMERI REALI: ESEMPIO 3 Rappresentazione come float di V = -29.1875 rappr. normalizzata: V = - .1110100112 *25 segno (1 bit): 1 mantissa (24 bit): .11101001 10000000 00000000 esponente (8 bit con eccesso 126) esp=5 126+5 = 131 10000011 segno 1 esponente 1000 0011 mantissa normalizzata (23 bit, MSB escluso) 110 1001 1000 0000 0000 0000 in memoria: byte 1 1100 0001 byte 2 1110 1001 byte 3 1000 0000 byte 4 0000 0000 NUMERI REALI: ESEMPIO 4 Rappresentazione come float di V = 0.110 rappr. normalizzata: V =.0(0011)2 periodico! segno (1 bit): 0 mantissa (24 bit): .11001100 11001100 11001100 esponente (8 bit con eccesso 126) esp=-3 126-3 = 123 01111011 segno 0 esponente 0111 1011 mantissa normalizzata (23 bit, MSB escluso) 100 1100 1100 1100 1100 in memoria: byte 1 0011 1101 byte 2 1100 1100 byte 3 1100 1100 byte 4 1100 1100 NUMERI REALI: ESEMPIO 4 Rappresentazione comeErrore float di V = 0.110 di troncamento o si tronca periodico! o si arrotonda rappr. normalizzata: V =.0(0011) 2 il C arrotonda segno (1 bit): 0 mantissa (24 bit): .11001100 11001100 11001101 esponente (8 bit con eccesso 126) esp=-3 126-3 = 123 01111011 segno 0 esponente 0111 1011 mantissa normalizzata (23 bit, MSB escluso) 100 1100 1100 1100 1101 in memoria: byte 1 0011 1101 byte 2 1100 1100 byte 3 1100 1100 byte 4 1100 1101 NUMERI REALI: ESEMPIO 5 Rappresentazione come float di V = 0.1510 rappr. normalizzata: V =.00(1001)2 periodico! segno (1 bit): 0 mantissa (24 bit): .10011001 10011001 10011010 esponente (8 bit con eccesso 126) esp=-2 126-2 = 124 01111100 segno 0 esponente 0111 1100 mantissa normalizzata (23 bit, MSB escluso) 001 1001 1001 1001 1001 1010 in memoria: byte 1 0011 1110 byte 2 0001 1001 byte 3 1001 1001 byte 4 1001 1010 NUMERI REALI: ESEMPIO 6 Rappresentazione come float di V = -1/310 rappr. normalizzata: V = -.(01)2 periodico! segno (1 bit): 1 mantissa (24 bit): .10101010 10101010 10101011 esponente (8 bit con eccesso 126) esp=-1 126-1 = 125 01111101 segno 1 esponente 0111 1101 mantissa normalizzata (23 bit, MSB escluso) 010 1010 1010 1010 1010 1011 in memoria: byte 1 1011 1110 byte 2 1010 1010 byte 3 1010 1010 byte 4 1010 1011 CONVERSIONE STRINGA / NUMERO Problema: data la rappresentazione di un numero reale in una certa base, determinare il valore del numero Soluzione: applicare la formula v n1 d B k m k k Bisogna considerare anche potenze negative di B, con le cifre dopo la virgola. CONVERSIONE STRINGA / NUMERO Esempio: calcolare il valore rappresentato dalla stringa 1001.112 v n1 d B k m k k Soluzione: si sommano i singoli contributi V = 23 + 20 + 2-1 + 2-2 = 9.7510 Operativamente, con i naturali si valutava il polinomio con il metodo di Horner. E adesso? CONVERSIONE STRINGA / NUMERO Conviene separare il calcolo della parte intera da quello della parte frazionaria: v 1 n1 d B d B k k m k k 0 k k Per il calcolo del valore della parte intera si può usare ancora l’algoritmo di Horner. Per il calcolo del valore della parte frazionaria si può adottare un algoritmo analogo. CONVERSIONE STRINGA / NUMERO Calcolo del valore della parte frazionaria: 1 d B k m k k ((..((d m / B) dm 1 )/ B) ...d1 ) / B) L’algoritmo di Horner raccoglieva via via il fattore B, questo raccoglie il fattore 1/B. Esempio: .11012 (valuta da destra a sinistra) V = (((((1 / 2 + 0) / 2) + 1) / 2 + 1) / 2) = 0.812510 CONVERSIONE NUMERO / STRINGA Per convertire un numero in una stringa di cifre, l’essenziale è riuscire a “isolare” e ricavare le singole cifre. Nel caso dei naturali, lo si fa con l’algoritmo delle divisioni successive: dk = vk % B il quoziente vk+1 = vk / B consente di iterare Per la parte frazionaria occorre dunque un algoritmo analogo. CONVERSIONE NUMERO / STRINGA Algoritmo delle moltiplicazioni successive si moltiplica v per B la parte intera che si genera costituisce la cifra più significativa la parte frazionaria itera il procedimento se prima o poi la parte frazionaria si azzera, il numero è rappresentabile in forma finita in tale base; se invece si rigenera la stessa serie di cifre, siamo di fronte a un numero periodico in tale base. UN ESEMPIO Calcolare la rappresentazione binaria del numero V=0.87510 .875 * 2 = 1.75 parte intera = 1, parte frazionaria restante = .75 .75 * 2 = 1.5 parte intera = 1, parte frazionaria restante = .5 .5 * 2 = 1.0 parte intera = 1, parte frazionaria restante = .0 Rappresentazione risultante: .1112 (non periodico) UN ALTRO ESEMPIO Calcolare la rappresentazione binaria del numero V=0.1510 .15 * 2 = 0.3 parte intera = 0 .3 * 2 = 0.6 parte intera = 0 .6 * 2 = 1.2 parte intera = 1 .2 * 2 = 0.4 parte intera = 0 .4 * 2 = 0.8 parte intera = 0 .8 * 2 = 1.6 parte intera = 1 .6 * 2 = 1.2 si ripete la sequenza!! Rappresentazione (periodica): .00(1001)2 DENTRO LA MACCHINA C E se volessimo “spiare” dentro la macchina virtuale C per vedere la rappresentazione fisica dei numeri? p Occorre x ricavare l’indirizzo della variabile esplorare quell’area di memoria byte per byte, per tanti byte quanta la dimensione di quel tipo di dato (es: float = 4 byte) visualizzare ogni byte. &x 9A 99 19 3E Esempio: x = 0,15 DENTRO LA MACCHINA C Un programma: main() { cast: ci serve un puntatore a byte, che in C si esprime come “unsigned char” float x; int i; unsigned char* p = (unsigned char*) &x; printf("Float: "); scanf("%f",&x); printf("\nRappr. interna di %f:\n", x); for (i=0; i<sizeof(x); i++) printf("Byte %i:\t%X\n",i, p[i] ); printf("\n"); i-esimo byte } sizeof: dà la dimensione in byte di quella variabile esadecimale OPERAZIONI FRA REALI & ERRORI Negli interi, si possono creare errori nelle operazioni, ma gli operandi sono comunque rappresentati esattamente Nei reali, invece, già gli stessi operandi possono essere affetti da errore, a causa dell’impossibilità di rappresentare le infinite cifre dei numeri periodici e irrazionali: è l’errore di troncamento. Errore di troncamento = ogni qual volta il numero di cifre disponibili è insufficiente. ERRORE DI TRONCAMENTO Si manifesta quando il numero è periodico il numero non è periodico ma ha troppe cifre il risultato di un’operazione, a causa un riporto, richiede troppe cifre Esempi (mantissa di 8 bit, per semplicità) 15.810 = .1111110011001100... * 24 301.510 = .10010110012 * 29 15110 + 16010 = = .10010111 * 28 + .10100000 * 28 = = .100110111 * 29 (è 15.75) (è 300) (è 310) OPERAZIONI FRA REALI & ERRORI Vi sono poi altre due sorgenti di errore: l’errore di incolonnamento è causato dalla necessità di incolonnare i numeri per poterli sommare o sottrarre l’errore di cancellazione è la conseguenza finale della presenza, a monte, di errori di troncamento, che possono far sì che alcune cifre del risultato non siano affidabili, ovvero siano “virtualmente cancellate” si manifesta sottraendo numeri simili fra loro. ERRORE DI INCOLONNAMENTO L’errore di incolonnamento è causato dalla necessità di incolonnare i numeri per poterli sommare o sottrarre per incolonnare due numeri che, in forma normalizzata, hanno esponente diverso occorre necessariamente “de-normalizzarne” uno si allinea quello di valore assoluto minore a quello di valore assoluto maggiore ciò causa una perdita di cifre significative nel numero che viene “de-normalizzato”. ERRORE DI INCOLONNAMENTO Esempio: 96.5 + 1.75 Ipotesi: mantissa di 8 bit (per semplicità) 96.510 =.110000012 * 27 (senza errore) 1.7510 =.111000002 * 21 (senza errore) Somma: .110000012 * 27 + .110000012 * 27 + .111000002 * 21 = .000000112 * 27 = cifre perse: è l’errore di incolonnamento .110001002 * 27 È 98, non 98.25 come doveva! ERRORE DI CANCELLAZIONE L’errore di cancellazione è può presentarsi quando si sottraggono numeri assai simili fra loro accade solo se in almeno uno dei due operandi, all’inizio, vi è stato errore di troncamento consiste nel fatto che si introducono zeri da destra per normalizzare il risultato, ma quegli zeri non sono significativi se ci fossero state le cifre perse all’inizio a causa del troncamento, il risultato sarebbe stato diverso. ERRORE DI CANCELLAZIONE Esempio: 15.8 + 15.5 Ipotesi: mantissa di 8 bit (per semplicità) 15.810 =.111111002 * 24 (con errore tronc.) 15.510 =.111110002 * 24 (senza errore) Differenza: cifre cancellate: è l’errore di .111111002 * 24 cancellazione 4 .111110002 * 2 = .000001002 * 24 = . 100000002 * 2-1 Sono 0 solo perché abbiamo troncato 15.8 all’inizio: avrebbero dovuto essere 11001 ERRORI: CONSEGUENZE A causa di questi errori, la proprietà associativa può non essere più verificata Esempio (mantissa di 8 bit, per semplicità) X = 0,75 .11000000 * 20 (senza errori) Y = 65,6 .10000011 * 27 (err. troncamento) Z = 64,0 .10000000 * 27 (senza errori) si ha che (X + Y) - Z è diverso da X + (Y - Z) ERRORI: CONSEGUENZE (X + Y) - Z è diverso da Primo caso: (X + Y) - Z Prima operazione: A = X + Y .11000000 * 20 + .10000011 * 27 = .00000001 * 27 + .10000011 * 27 = .10000100 * 27 A (err. incolonnamento) Seconda operazione: R = A - Z .10000100 * 27 .10000000 * 27 = .00000100 * 27 = .100????? * 22 = .10000000 * 22 R (da rinormalizzare) (errore cancellazione) R = .10000000 * 22 X + (Y - Z) Secondo caso: X + (Y - Z) Prima operazione: A = Y - Z .10000011 * 27 .10000000 * 27 = .00000011 * 27 = .11?????? * 21 = .11000000 * 21 A (da rinormalizzare) (errore cancellazione) Seconda operazione: R = X + A .11000000 * 20 + .11000000 * 21 = .01100000 * 21 + .11000000 * 21 = 1.00100000 * 21 .10010000 * 22 R (err. incolonnamento) (da rinormalizzare) (err. tronc. potenziale) R = .10010000 * 22 ACCUMULAZIONE DI ERRORI La presenza di errori che si accumulano può portare a risultati totalmente assurdi Esempio: Calcolo di con l’algoritmo di Euclide Una circonferenza di raggio 1 è lunga 2, che può essere approssimata: dall’esterno, dal perimetro del poligono regolare di n lati circoscritto dall’interno, dal perimetro del poligono regolare di n lati inscritto ACCUMULAZIONE DI ERRORI Valgono le relazioni: ln = lato del poligono di n lati inscritto Ln = lato del poligono di n lati circoscritto = 2 l / (4 - l2) Ln ln Ln = lunghezza lato poligono circoscritto di n lati ln = lunghezza lato poligono inscritto di n lati ACCUMULAZIONE DI ERRORI Una funzione che implementa l’algoritmo: void pigrecoFloat(void) { float eps, LN, smpinf, smpsup, nlati, OC2, diff; printf("Calcolo di pigreco con FLOAT. " "Precisione [1e-8] ? "); scanf("%f", &eps); nlati = 4.0; LN = sqrt(2.0); do { OC2 = sqrt(4.0 - LN * LN); nlati *= 2.0; diff = 2.0 - OC2; LN = sqrt(diff); smpinf = LN * nlati / 2.0; smpsup = LN * nlati / OC2; printf("nl=%10.0f d2=%f piInf=%f piSup=%f\n", nlati, OC2, smpinf, smpsup); } while ((smpsup-smpinf >= eps) && (nlati < 1e+19)); } ACCUMULAZIONE DI ERRORI … e il suo output: Calcolo di pigreco con nl= 8 d2=1.414214 nl= 16 d2=1.847759 nl= 32 d2=1.961571 nl= 64 d2=1.990369 nl= 128 d2=1.997591 nl= 256 d2=1.999398 nl= 512 d2=1.999849 nl= 1024 d2=1.999962 nl= 2048 d2=1.999991 nl= 4096 d2=1.999998 nl= 8192 d2=1.999999 nl=16384 d2=2.000000 nl=32768 d2=2.000000 FLOAT. Precisione ? 1E-8 piInf=3.061467 piSup = 4.329569 piInf=3.121444 piSup = 3.378627 piInf=3.136546 piSup = 3.197995 piInf=3.140333 piSup = 3.155528 piInf=3.141286 piSup = 3.145074 piInf=3.141519 piSup = 3.142465 piInf=3.141208 piSup = 3.141444 piInf=3.142451 piSup = 3.142510 piInf=3.142451 piSup = 3.142466 piInf=3.162278 piSup = 3.162282 piInf=3.162278 piSup = 3.162279 piInf=2.828427 piSup = 2.828427 piInf=0.000000 piSup = 0.000000 CONVERSIONE DA INTERI A REALI Nelle espressioni che coinvolgono interi e reali, i numeri interi devono essere convertiti in rappresentazione reale per poter eseguire le operazioni Non si può semplicemente “spostare la virgola”, perché la rappresentazione in complemento a due non è posizionale Esempio: N = -8 (intero, 1 byte) R = -8.0 = .1*23 segno 11111000 1 10000001 0000000 esp (126+3) mantissa ESERCIZIO Dire come vengono svolte le seguenti espressioni, calcolandole passo passo Ipotesi: interi rappresentati in complemento a due su un byte (8 bit da -128 a +127) reali rappresentati su due byte (1 bit di segno, 8 di esponente con eccesso 126, 7 di mantissa) Esercizio int i=10; float a=0.6, b, c; b = a + i - 8; c = a + (i - 8); FUNZIONI DI CONVERSIONE STANDARD La libreria standard stdlib fornisce quasi tutte le funzioni di conversione già pronte da stringa a numero atoi() atol() atof() da stringa a intero da stringa a long da stringa a double da numero a stringa (solo Turbo C) itoa() ltoa() fcvt() da intero a stringa da long a stringa da double a stringa Il C standard usa sprintf(), che vedremo più avanti.