Capitolo 1 I NUMERI 1.1 Basi di numerazione
Transcript
Capitolo 1 I NUMERI 1.1 Basi di numerazione
Capitolo 1 I NUMERI 1.1 Basi di numerazione Un numero è una sequenza finita di simboli detti cifre. I simboli utilizzati dipendono dal sistema di numerazione utilizzato. Il sistema di numerazione usualmente utilizzato è detto posizionale decimale, dove posizionale indica che il valore di una cifra dipende dalla posizione che essa ha all’interno di un certo numero, e decimale indica che la base utilizzata è b = 10. I simboli utilizzati in base 10 sono 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9. Ad esempio, se scriviamo il numero 9102.917 intendiamo esprimere il numero 9 × 103 + 1 × 102 + 0 × 101 + 2 × 100 + 9 × 10−1 + 1 × 10−2 + 7 × 10−3 Si noti che la notazione posizionale non è l’unica esistente. Esistono anche altri sistemi di numerazione, ad esempio la numerazione romana, in cui ogni cifra del numero rappresenta sempre il proprio valore (M = 1000, X = 10, V = 5, I = 1, . . .). Nulla vieta, per rappresentare un numero reale a ∈ R, di utilizzare basi diverse dalla decimale. Sia dunque b ∈ N una base qualsiasi. Per rappresentare i numeri in base b sono necessari esattamente b simboli. Se 2 ≤ b ≤ 10 si usano come simboli le prime b cifre della base decimale, ovvero i simboli 0, 1, . . . , b − 1. Se b > 10, si aggiungono alle 10 cifre decimali le lettere maiuscole dell’alfabeto. Ad esempio: 1 2 Capitolo 1. I numeri b 2 8 16 cifre 01 01234567 0123456789ABCDEF Quindi, generalizzando, un numero a ∈ R può essere espresso in base b nella forma a = ± (an an−1 an−2 . . . a1 a0 . a−1 a−2 . . .)b = ± n X ai bi i=−∞ con ai cifre della base b. La parte che precede il punto di radice viene detta parte intera e quella che segue il punto di radice viene detta parte frazionaria. Se lavoriamo con una base generica b, è importante indicare in quale base è espresso un numero dato, in quanto il suo valore varia al variare di b. Quando vi possono essere ambiguità nella scrittura, è opportuno indicare come pedice il valore di b. Ad esempio: 123.110 = 1 × 102 + 2 × 101 + 3 × 100 + 1 × 10−1 = 123.110 123.17 = 1 × 72 + 2 × 71 + 3 × 70 + 1 × 7−1 = 66.142857142 . . .10 123.14 = 1 × 42 + 2 × 41 + 3 × 40 + 1 × 4−1 = 27.2510 È anche evidente che lo stesso numero necessita di un numero di cifre maggiore o uguale, al diminuire della base b. Ad esempio: 25310 1.1.1 = = = FD16 3758 111111012 Cambiamento di base Si è visto che lo stesso numero reale può essere rappresentato rispetto a differenti basi. Noi siamo ovviamente abituati ad operare in base 10, ma, ad esempio, i computer lavorano con numeri espressi in base 2 e quindi è importante conoscere le modalità di passaggio da una base all’altra e le problematiche che tali cambiamenti di base possono creare. È noto che i numeri reali si possono suddividere in due sottoinsiemi: i numeri razionali (insieme Q) ed i numeri irrazionali (insieme I). A loro volta i numeri razionali possono essere divisi in due categorie: i numeri a parte frazionaria finita ed i numeri periodici (e come tali illimitati). 1.1. Basi di numerazione 3 Per ambedue le categorie è possibile trovare la frazione generatrice (cioè n n la frazione tale che a = , con n ed m interi, espressi in base b): m m Numeri finiti: Dato un numero a, rappresentato in base b, con parte frazionaria finita, la frazione generatrice ha come numeratore tutto il numero (privato del punto di radice e degli zeri davanti alla prima cifra significativa) e, come denominatore, un 1 seguito da tanti zeri quante sono le cifre che formano la parte frazionaria. Ad esempio calcoliamo la frazione generatrice del numero 73.4568 ed anche la corrispondente frazione generatrice decimale (ottenuta convertendo numeratore e denominatore in base 10): ¶ µ ¶ µ ¶ µ 30510 15255 73456 −3 = = 73.4568 = 734568 × 8 = 1000 8 512 10 256 10 Numeri periodici: Dato un numero a, periodico in base b, la frazione generatrice ha per numeratore tutto il numero (privato del punto di radice e degli zeri davanti alla prima cifra significativa) meno il numero ottenuto togliendo le cifre del periodo (e trascurando il punto di radice) e per denominatore il numero formato da tante cifre b − 1 quante sono le cifre del periodo, seguite da tanti zeri quante sono le cifre dell’antiperiodo (che è la parte che segue il punto di radice e che precede il periodo). Ad esempio: Sia b = 16. Calcoliamo le frazioni generatrici dei numeri 0.2D16 e 0.AE16 ed anche le corrispondenti frazioni generatrici decimali: Primo numero: 0.2D16 = µ 2D − 2 F0 ¶ = 16 µ 2B F0 ¶ = 16 µ 43 240 ¶ µ ¶ 10 Secondo numero: 0.AE16 = µ AE − A F0 ¶ = 16 µ A4 F0 ¶ 16 = 41 60 10 Il passaggio da una base b1 ad una base b2 non conserva necessariamente la tipologia del numero e questo, come vedremo, farà parte di una delle caratteristiche da tener presente quando si lavora con i computer. Vediamo alcune regole da ricordare relativamente al cambiamento di base di un numero: 4 Capitolo 1. I numeri • Se un numero è illimitato non periodico in una certa base, lo è anche in tutte le altri basi possibili. • Un numero finito in una certa base, può essere finito oppure periodico in un’altra base. • Un numero periodico in una certa base, può essere periodico oppure finito in un’altra base. Dato un numero razionale a espresso in base b1 , è possibile sapere anticipatamente se esso è, o meno, finito in base b2 : n con n ed m Si calcola la frazione generatrice decimale, ovvero a = m espressi in base 10. Se m risulta essere una potenza di b2 (ovvero m = be2 con e ∈ N) allora in base b2 il numero sarà finito. Ad esempio: Esempio 1 Controlliamo se il numero decimale finito 872.4310 è finito anche in base 2. ¶ µ ¶ µ 87243 87243 = 872.4310 = 100 10 22 · 52 10 e poichè tale frazione è irriducibile, il numero finito in base decimale non lo è certamente in base binaria. Esempio 2 Calcoliamo in quale/i base/i il numero decimale periodico 0.4̄10 è finito. µ ¶ µ ¶ 4 4 0.410 = = 9 10 32 10 pertanto il numero è finito in base 3 ed in base 9 e si ha 0.410 = 0.113 = 0.49 Vediamo ora come convertire un numero reale da una base b1 ad una base b2 . Vi sono differenti modalità per effettuare la conversione a seconda dei valori assunti da b1 e da b2 . Caso b2 = 10 Numero finito: Vi sono due possibili algoritmi per calcolare il valore decimale (che potrebbe non essere finito) di un numero reale (finito) espresso in base qualsiasi b1 : 1.1. Basi di numerazione 5 1. Utilizzo della notazione posizionale: si calcola il valore utilizzando la definizione a = ± (an an−1 an−2 . . . a1 a0 .a−1 a−2 . . . a−m )b = ± n X ai bi i=−m Vediamo alcuni esempi: Esempio 1 2401.23145 = 2 × 53 + 4 × 52 + 1 + 3 1 4 2 + 2 + 3 + 4 = 351.534410 5 5 5 5 Esempio 2 110110.1012 = 1 × 25 + 1 × 24 + 1 × 22 + 1 × 2 + 1 1 + 3 = 54.62510 2 2 Esempio 3 21.213 = 2 × 3 + 1 + 1 2 + 2 = 7.710 3 3 2. Utilizzo dell’Algoritmo di Horner: si considerano separatamente la parte intera e la parte frazionaria del numero e poi si procede come segue: Parte intera: Partendo dalla prima cifra significativa alla sinistra, verso destra, si moltiplica il valore per la base b1 e si aggiunge la cifra successiva. Il risultato si moltiplica per la base b1 e si aggiunge la cifra successiva, e cosı̀ via sino all’esaurimento delle cifre. Ovvero (an an−1 an−2 . . . a1 a0 )b = ((· · · ((an ×b1 +an−1 )×b1 +an−2 )×b1 +· · · )×b1 +a1 )×b1 +a0 Parte frazionaria: Partendo dalla prima cifra significativa alla destra della parte frazionaria, verso sinistra, fino al punto di radice si divide il valore per la base b1 e si aggiunge la cifra precedente. Il risultato si divide per la base b1 e si aggiunge la cifra precedente e cosı̀ via sino all’esaurimento delle cifre, terminando con una divisione per b1 . Ovvero (0.a−1 a−2 a−3 . . . a−m+1 a−m )b = ((· · · ((a−m : b1 +a−m+1 ) : b1 +a−m+2 ) : b1 +· · · ) : b1 +a−1 ) : b1 6 Capitolo 1. I numeri Vediamo un esempio: 110110.0012 = parte intera → → parte frazionaria → → ((((1×2 +1)× 2 +0)× 2 +1)× 2 +1)× 2 + 0 5410 ((1 : 2 + 0) : 2 + 0) : 2 0.12510 = 54.12510 Numero periodico: Supponiamo ora che il numero espresso in base b1 abbia parte frazionaria periodica. Come possiamo procedere per calcolare la parte frazionaria decimale, visto che essendo illimitata la parte frazionaria in base b1 , non possiamo utilizzare nè la notazione posizionale nè l’algoritmo di Horner? Possiamo far uso della regola di determinazione della frazione generatrice decimale di un numero periodico in base qualsiasi precedentemente indicata. Vediamo qualche esempio: Esempio 1 Convertiamo il numero 0.14 in base b2 = 10: µ ¶ µ ¶ µ ¶ 1 3 1 = = = 0.310 0.14 = 3 4 3 10 9 10 Esempio 2 Convertiamo il numero 0.2D16 in base b2 = 10: µ ¶ ¶ µ ¶ µ 2D − 2 2B 43 0.2D16 = = = = 0.1791610 F0 F0 16 240 10 16 Esempio 3 Convertiamo il numero 4.315 in base b2 = 10: ¶ µ ¶ µ ¶ µ 333 93 431 − 43 = = = 4.6510 4.315 = 40 40 5 20 10 5 Caso b1 = 10 Numero finito: Sia dato un numero decimale finito che si vuole convertire in base b2 (e in tale base potrebbe essere non finito). Bisogna anzitutto distinguere la parte intera dalla parte frazionaria ed operare in modo differente: 1.1. Basi di numerazione 7 Parte intera: L’algoritmo di conversione ha sempre termine e consiste nell’effettuare iterativamente delle divisioni per la nuova base b2 , fermandosi solo quando si ottiene un quoziente nullo. I resti delle divisioni effettuate, presi in ordine inverso a quello con cui sono stati calcolati, formano la parte intera del numero convertito. Ovviamente i resti ottenuti sono dei numeri decimali il cui valore è < b2 . Se b2 > 10, per formare il numero nella nuova base, ai resti decimali vanno sostituiti (se necessario) i corrispondenti simboli della nuova base di numerazione. Parte frazionaria: Si moltiplica ripetutamente per la base b2 e si tolgono le parti intere che, prese nell’ordine, formeranno la parte frazionaria del numero espresso nella nuova base b2 . Anche in tal caso le parti intere ottenute sono dei numeri decimali il cui valore è < b2 e, quando b2 > 10, per formare il numero nella nuova base ad essi vanno sostituiti (se necessario) i corrispondenti simboli della nuova base di numerazione. Come è facilmente intuibile, non è assolutamente certo che tale procedura termini dopo un numero finito di passi. Vi sono infatti due possibilità: 1. Ad un certo passo si ottiene come valore del prodotto un numero con parte frazionaria nulla. In questo caso il procedimento si arresta ed il numero nella nuova base sarà finito. 2. Ad un certo passo si ottiene come valore del prodotto un numero con parte frazionaria precedentemente già trovata durante i passi precedenti. In questo caso si è individuato il periodo della parte frazionaria del numero espresso nella nuova base b2 . Vediamo ora alcuni esempi, utilizzando le seguenti convenzioni grafiche: Nella procedura di divisione per la parte intera del numero da convertire, con una freccia ascendente si indica che i resti ottenuti devono essere considerati in ordine inverso. Nella procedura di moltiplicazione per la parte frazionaria, con una freccia discendente si indica che le parti intere dedotte dai risultati delle moltiplicazioni devono essere considerate nell’ordine con il quale esse sono state ottenute; tali parti intere risultano evidenziate nel prodotto tramite una sottolineatura delle stesse e vengono ovviamente sottratte dal prodotto prima di effettuare la successiva moltiplicazione; i caratteri ? posti a fianco di un certo numero di parti intere 8 Capitolo 1. I numeri contigue indicano che tali cifre formeranno il periodo del numero convertito; l’eventuale periodo viene individuato quando si trova un valore da moltiplicare che sia già stato considerato e i due valori coincidenti vengono indicati da un simbolo ◦ che li precede. Esempio 1 Convertiamo il numero 967.7812510 in base b2 = 16: Divisioni Quozienti Resti 967 ÷ 16 60 710 = 716 6 60 ÷ 16 3 1210 = C16 3 ÷ 16 0 310 = 316 Moltiplicazione Parti intere 0.78125 ×16 = 12.5 1210 = C16 810 = 816 ? 0.5 ×16 = 8.0 0.0 Quindi 967.7812510 = 3C7.C816 = 3.C7C816 × 162 Esempio 2 Convertiamo il numero 93.62510 in base b2 = 2: Divisioni Quozienti Resti 93 ÷ 2 46 1 6 46 ÷ 2 23 0 23 ÷ 2 11 1 11 ÷ 2 5 1 5÷2 2 1 2÷2 1 0 1÷2 0 1 Moltiplicazione Parti intere 0.625 ×2 = 1.25 1 0 0.25 ×2 = 0.5 0.5 ×2 = 1.0 1? 0.0 Quindi si ottiene 93.62510 = 1011101.1012 = 0.10111011012 × 27 1.1. Basi di numerazione 9 Esempio 3 Convertiamo il numero 20.02510 in base b2 = 2: Divisioni Quozienti Resti 20 ÷ 2 10 0 6 10 ÷ 2 5 0 5÷2 2 1 2÷2 1 0 1÷2 0 1 Moltiplicazione 0.025 ×2 = 0.05 ×2 = 0.1 ×2 = ◦ 0.2 ×2 = 0.4 ×2 = 0.8 ×2 = 0.6 ×2 = ◦ 0.2 Parti intere 0.05 0 0 0.1 0 0.2 0 0.4 0.8 0 1 1.6 1 1.2 ? ? ? ? ? Avendo trovato un valore frazionario da moltiplicare, precedentemente già considerato (il valore 0.2), la parte frazionaria finita del numero dato risulta essere periodica in base 2. Si ottiene quindi 20.02510 = 10100.00000112 Esempio 4 Convertiamo il numero 7.7610 in base b2 = 7: Divisioni Quozienti Resti 7÷7 1 0 6 1÷7 0 1 Moltiplicazione ◦ 0.76 ×7 = 0.32 ×7 = 0.24 ×7 = 0.68 ×7 = ◦ 0.76 5.32 2.24 1.68 4.76 Parti intere 5 2 1 4 ? ? ? ? ? 10 Capitolo 1. I numeri Avendo trovato un valore frazionario considerato in precedenza (il valore 0.76), la parte frazionaria finita del numero dato risulta essere periodica in base 7. Quindi si ottiene 7.7610 = 10.52147 Numero periodico: Supponiamo ora che il numero decimale abbia parte frazionaria periodica. Per la parte intera possiamo procedere come nel caso del numero finito. Ma come possiamo procedere per calcolare la parte frazionaria nella nuova base b2 ? Possiamo ancora usare il precedente algoritmo di moltiplicazione, ma lavorando con i cosiddetti numeri misti (intero+frazione propria). Vediamo qualche esempio: Esempio 1 Convertiamo il numero 0.310 in base b2 = 2: Moltiplicazione 2 1 ×2 = ◦ 3 3 2 3 ◦ ×2 = 4 3 Parti intere = 2 0+ 3 0 ? = 1+ 1 3 1 ? ? 1 3 ottenendo 0.310 = 0.012 Esempio 2 Convertiamo il numero 0.610 in base b2 = 2: Moltiplicazione 2 4 ◦ ×2 = 3 3 2 3 1 3 ◦ ×2 = Parti intere = 1 1+ 3 1 ? = 0+ 2 3 0 ? ? 2 3 ottenendo 0.610 = 0.102 1.1. Basi di numerazione 11 Caso generale Non vi sono algoritmi particolari qualora b1 o b2 non siano uguali a 10. In tale caso la conversione deve essere fatta in due tappe, passando attraverso la base 10 (ovvero b1 → 10 → b2 ). La base 2 e le sue potenze sono molto utilizzate in informatica. Nel caso in cui si debba passare dalla base 2 alla base 2k (o viceversa) esiste una procedura pressochè immediata (senza dover passare per la base 10). Quando la base considerata è 2k , per rappresentare le sue 2k −1 cifre sono necessarie, in base 2, delle k-uple di cifre binarie. Ad esempio se b = 4 = 22 le cifre 0, 1, 2, 3 in tale base sono rappresentabili dalle 4 coppie binarie (00)2 , (01)2 , (10)2 e (11)2 . Se b = 16 = 24 le cifre in tale base sono rappresentabili dalle 16 quaterne binarie (0000)2 , (0001)2 , (0010)2 , . . . , (1110)2 e (1111)2 . Dato quindi un numero in base 2, per trasformarlo in base 2k è sufficiente, a partire dal punto di radice, verso sinistra per la parte intera e verso destra per la parte frazionaria (aggiungendo eventualmente gli zeri necessari), raggruppare delle k-uple di cifre binarie e poi sostituire ad esse la corrispondente cifra nella nuova base. Ad esempio: Convertiamo il numero 10001.1101110112 in base b2 = 16 = 24 : 0001 |{z} . 1101 |{z} 1101 |{z} 1000 |{z} |{z} 0001 ↓ ↓ ↓ ↓ ↓ 1 1 . D D 8 e si ha 10001.1101110112 = 11.DD816 = 0.11DD816 × 162 Il procedimento inverso di passaggio da una base 2k alla base 2 è similare, ma in tal caso si costruiscono, a partire dalle cifre della base 2k , le k-uple di cifre binarie. Ad esempio: Convertiamo il numero 21.6738 in base b2 = 2: e si ottiene 2 1 . 6 7 3 ↓ ↓ ↓ ↓ ↓ z}|{ z}|{ z}|{ z}|{ z}|{ 010 001 . 110 111 011 21.6738 = 10001.1101110112 = 0.10001110111011 × 25 12 Capitolo 1. I numeri Naturalmente tale procedimento può essere utilizzato anche per passare da una base b1 = 2k1 ad una base b2 = 2k2 , con due passi: prima di passa dalla base b1 alla base 2 e poi dalla base 2 alla base b2 . Un altro vantaggio di tale conversione pressochè immediata si nota in presenza di numeri con parte frazionaria periodica in quanto, in tal caso, anche nella nuova base il numero sarà periodico ed il suo periodo è facilmente identificabile. Ad esempio: Convertiamo il numero 1632.63148 in base b2 = 2: e quindi 1 6 3 2 . 6 3 1 4 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ z}|{ z}|{ z}|{ z}|{ z}|{ z}|{ z}|{ z}|{ 001 110 011 010 . 110 011 001 100 1632.63148 = 1110011010.11002 = 0.111001101011002 × 210 1.1.2 Normalizzazione ed approssimazione Un numero a ∈ R, rappresentato in base b, non ha una rappresentazione unica. Infatti, ricordiamo che quando si sposta il punto di radice a destra di una posizione, il numero risulta moltiplicato per la base, e che quando si sposta il punto di radice a sinistra di una posizione, il numero risulta diviso per la base. Ad esempio, per uno stesso numero espresso in base b ≥ 8, si hanno le seguenti rappresentazioni: 1234.57b × b5 = 0.123457b × b9 = 123457b × b3 = 1.23457b × b8 = 123457000b × b0 = 0.00123457b × b11 = · · · È possibile decidere che tutti i numeri vengano rappresentati in una stessa forma (ovvero in forma normalizzata). Ad esempio si può decidere di adottare la convenzione che il numero debba essere rappresentato sempre con prima cifra significativa (ovvero cifra non nulla) alla destra del punto di radice ovvero nella forma a = ±(0.a1 a2 a3 . . .)b × be = ± (0.m)b × be con a1 6= 0. e viene detto esponente o caratteristica ed m viene detta mantissa. 1.1. Basi di numerazione 13 Con questa normalizzazione è possibile rappresentare tutti i numeri reali, eccettuato lo 0. La normalizzazione non modifica la natura del numero reale. Pertanto i numeri scritti in forma normalizzata possono avere mantissa finita, oppure periodica, oppure illimitata (ad esempio a = π). Prima di proseguire, si desidera sottolineare quanto segue: consideriamo un certo numero a ∈ Q espresso, ad esempio, in base 10. La sua rappresentazione, anche se si fissa la normalizzazione, può non essere univoca. Vediamo questo esempio: Sia dato il numero 0.11910 . Apparentemente è un numero periodico con parte frazionaria illimitata (0.119999999 . . .). Calcoliamo la sua frazione generatrice. µ ¶ µ ¶ 119 − 11 108 0.11910 = = = 0.1210 900 900 10 10 Questo significa che 0.11910 e 0.1210 sono due rappresentazioni, con la medesima normalizzazione, dello stesso numero. Esiste una regola generale per tale fenomeno: la rappresentazione normalizzata non è unica per tutti i numeri reali decimali il cui periodo sia 9 e, se i numeri sono espressi in base b, per tutti i numeri reali il cui periodo sia uguale a b − 1. In tutti gli altri casi, la normalizzazione determina univocamente m ed e. Supponiamo ora di poter conservare solo un numero t di cifre significative della mantissa di a (escludendo il punto di radice e lo zero che lo precede) e quindi di considerare una approssimazione di a del tipo: a∗ = ±(0.a1 a2 a3 . . . at )b × be = ± (0.m∗ )b × be = ± m∗b × be−t dove m è un intero formato dalle t cifre a1 a2 a3 . . . at . Vi sono due modi per approssimare un numero conservando solo t cifre di mantissa: Troncamento: Vengono banalmente trascurate nella mantissa m, tutte le cifre successive ad at . Ad esempio: t a∗ a (0.4567894251 . . .)10 3 0.45610 6 0.45678910 8 0.4567894210 14 Capitolo 1. I numeri Arrotondamento: Si usa la seguente regola: ½ at e ∗ a = ±(0.a1 a2 a3 . . . at )b · b dove at = at + 1 se at+1 < b/2 se at+1 ≥ b/2 at+1 è ovviamente la cifra che segue at , ovvero la prima cifra trascurata della mantissa. Ad esempio: t a∗ a (0.4567894251 . . .)10 3 0.45710 6 0.45678910 8 0.4567894310 1.1.3 Errore assoluto e relativo Dato un numero reale decimale a ed una sua approssimazione a∗ si definisce Errore assoluto: la quantità εa = |a − a∗ | Errore relativo: la quantità εr = |a − a∗ | , |a| con a 6= 0 In generale, l’errore relativo fornisce un’indicazione più realistica di “quanto si sbaglia” considerando a∗ al posto di a, in quanto esso non viene influenzato dall’ordine di grandezza dei numeri che si stanno considerando ed è legato unicamente al numero di cifre di a∗ , a partire dalla prima, che coincidono con quelle di a. Per conoscere quante cifre significative decimali in comune vi sono tra un numero a ed una sua approssimazione a∗ , è sufficiente calcolare il valore − log10 εr . Consideriamo il seguente esempio (errori e logaritmo decimale sono approssimati a 3 cifre significative): a∗ εa εr −log10 εr a −30 0.456789425 × 10 0.4567895 × 10−30 7.5 × 10−38 1.64 × 10−7 6.79 0.456789425 × 10−30 0.6 × 10−30 1.43 × 10−31 3.14 × 10−1 0.5 1.2. Algoritmi 15 I valori di a e di a∗ sono molto piccoli. Nel secondo caso l’approssimazione a∗ non ha nessuna cifra significativa in comune con a, ma guardando l’errore assoluto esso sembrerebbe essere piccolo (dell’ordine di 10−31 ). L’errore relativo mette giustamente in luce la scarsa accuratezza dell’approssimazione. Si consideri ora l’esempio seguente a∗ εa εr −log10 εr a +30 0.456789425 × 10 0.4567895 × 10+30 7.5 × 10+22 1.64 × 10−7 6.79 0.456789425 × 10+30 0.6 × 10+30 1.43 × 10+29 3.14 × 10−1 0.5 Sia a che a∗ sono valori molto grandi. Guardando gli errori assoluti (che sono grandi), sembrerebbe che entrambe le approssimazioni non fossero buone. L’errore relativo (ed il logaritmo), invece, mostrano come la prima approssimazione sia accettabile. Si noti anche che gli errori relativi di questo esempio coincidono esattamente con quelli dell’esempio precedente. Se consideriamo come approssimazione a∗ il valore ottenuto per troncamento o arrotondamento a t cifre della mantissa normalizzata si hanno le seguenti relazioni Errore assoluto: εa = |a − a∗ | ≤ c × be−t Errore relativo: εr = |a − a∗ | ≤ c × b1−t |a| 1 se si lavora per arrotondamento e c = 1 se si lavora per con c = 2 troncamento. 1.2 1.2.1 Algoritmi Conversione da base 10 a base b di un numero intero Siano dati a ∈ Z (in base 10) e b ≥ 2. Si desidera convertire in base b il valore assoluto di a. Applichiamo il procedimento delle divisioni ripetute ad |a| ed otteniamo il seguente algoritmo. 16 Capitolo 1. I numeri read a ∈ Z read b ≥ 2 a = |a| while a j6= 0k do a q= b r =a−q×b print r a=q end while ¹ º x dove indica la parte intera della divisione tra x ed y. I resti iny teri stampati, presi nell’ordine inverso (sostituendo eventualmente il corrispondente simbolo per le basi b > 10), danno le cifre del numero convertito in base b. 1.2.2 Conversione da base b a base 10 di un numero intero, con l’algoritmo di Horner Siano dati a ∈ Z, con |a| = (an an−1 . . . a0 )b (rappresentato con n + 1 cifre della base b). Si desidera calcolare il valore decimale del numero dato, utilizzando l’algoritmo di Horner. read a ∈ Z read n ∈ N read b ≥ 2 x = an for i = n − 1, . . . , 0 step −1 x = x × b + ai end for print sign(a)x dove sign(a) rappresenta il segno di a. Le varie cifre del numero a vanno fornite, per le basi b > 10, sostituendo eventualmente il corrispondente valore decimale delle cifre non numeriche. Il valore sign(a)x corrisponde al numero convertito in base decimale. 1.2.3 Calcolo del valore di un polinomio in un punto Sia dato un polinomio P di grado al più n (P ∈ P n ) scritto nella forma P (x) = α0 xn + α1 xn−1 + · · · + αn−1 x + αn , (1.1) 1.3. Esercizi 17 ed un punto x̄. Si vuole valutare P (x̄) ovvero il valore del polinomio nel punto x̄. Possiamo applicare l’espressione (1.1) ovvero calcolare n X P (x̄) = αi x̄n−i i=0 effettuando j − 1 moltiplicazioni per ogni potenza xj (j = 1, . . . , n), n moltiplicazioni per i prodotti relativi ai coefficienti ed infine n addizioni, ovvero, in totale, n2 + n/2 moltiplicazioni ed n addizioni. In alternativa possiamo utilizzare il seguente schema iterativo ½ β0 = α 0 βi = x̄ βi−1 + αi , i = 1, . . . , n ottenendo βn = P (x̄). Con tale procedimento vengono eseguite solamente n moltiplicazioni ed n addizioni. Tale algoritmo corrisponde esattamente alla regola di Ruffini per trovare il polinomio quoziente Q(x) ed il resto r della divisione di P (x) per (x−x̄), con P (x) = Q(x)(x− x̄)+r, Q(x) = β0 xn−1 +β1 xn−2 +· · ·+βn−2 x+βn−1 , r = P (x̄) ed anche all’algoritmo di Horner già illustrato nelle conversioni della parte intera da base b a base 10. Si ha quindi read n read αi , i = 0, . . . , n read x̄ β = α0 for i = 1, . . . , n β = x̄ × β + αi end for print β 1.3 Esercizi Esercizio 1.1 Si scriva un algoritmo per la conversione da base 10 a base b della parte frazionaria di un numero. Esercizio 1.2 Si scriva un algoritmo per la conversione da base b a base 10 della parte frazionaria di un numero, con l’algoritmo di Horner. Esercizio 1.3 Sapendo che se P (x) = Q(x)(x − x̄) + r, allora P 0 (x) = Q0 (x)(x − x̄) + Q(x) e quindi P 0 (x̄) = Q(x̄) si scriva un algoritmo che calcoli il valore della derivata prima del polinomio P (x) nel punto x̄. 18 Capitolo 1. I numeri Capitolo 2 ARITMETICA DEL COMPUTER 2.1 Rappresentazione ed aritmetica dei numeri interi In questo contesto non ci interessa dare una trattazione completa delle varie forme di rappresentazione degli interi nel computer. Ci limitiamo a dare alcune indicazioni che valgono per la quasi totalità dei computer attuali. In un computer i numeri interi vengono memorizzati come numeri binari in rappresentazione complemento a due. Per tale memorizzazione sono a disposizione un certo numero finito k di cifre binarie (bit = binary digit), compreso il bit per rappresentare il segno del numero intero. Non tutti i numeri interi sono quindi rappresentabili, ma solo quelli compresi tra nmin = −2k−1 e nmax = 2k−1 − 1. Gruppi di 8 bit vengono chiamati byte. Una word (o parola) può essere di 16 bit (2 byte) o di 32 bit (4 byte), e la sua lunghezza dipende dall’architettura del computer. La maggior parte dei computer è in grado di utilizzare k = 16 bit oppure k = 32 bit per memorizzare i numeri interi, qualsiasi sia la lunghezza della word. Con taluni linguaggi è possibile richiedere altri valori per k (ad esempio k = 8 o k = 64). Gli interi che appartengono all’intervallo [nmin , nmax ] sono rappresentati esattamente (nessun errore). Se si cerca erroneamente di memorizzare un numero n < nmin (n > nmax ) si ha la cosiddetta situazione di underflow (rispettivamente overflow). Gli intervalli di interi rappresentabili per k = 16 e k = 32, con una rappresentazione complemento a due, sono: 19 20 Capitolo 2. Aritmetica del computer byte bit 2 16 32 4 nmin nmax 15 −2 = −32 768 215 − 1 = 32 767 31 −2 = −2 147 483 648 231 − 1 = 2 147 483 647 Per quanto riguarda le operazioni aritmetiche tra interi (addizione, sottrazione, moltiplicazione e divisione intera) esse danno sempre luogo ad un risultato esatto. L’unico caso di errore può avvenire qualora il risultato dell’operazione sia un numero esterno all’intervallo [nmin , nmax ], con impossibilità di rappresentare tale risultato e quindi con situazione di underflow o di overflow. 2.2 Rappresentazione dei numeri reali Come vedremo, l’utilizzo dei numeri reali su di un computer può essere, a differenza dei numeri interi, causa di numerosi problemi. Usualmente i computer utilizzano per i numeri reali una rappresentazione normalizzata in base binaria (b = 2). Talvolta viene usata una normalizzazione in base esadecimale (b = 16), anche se poi la mantissa viene memorizzata sul computer trasformando in binario le rispettive cifre esadecimali. Sia fissata una certa normalizzazione ed una certa base b per il numero reale a, ad esempio a = ± (0.a1 a2 a3 . . .)b × be = ± (0.m)b × be (la normalizzazione può comunque variare da un computer ad un altro). Il numero di bit che sono a disposizione in un computer per rappresentare un numero reale è sempre finito. Esso viene suddiviso logicamente in tre parti in cui vengono rappresentati, rispettivamente, il segno del numero (sempre un solo bit che viene posto uguale a zero, per i numeri positivi, ed uguale a uno, per i numeri negativi), l’esponente e la mantissa: ± esponente mantissa Il numero di cifre a disposizione per la mantissa e per l’esponente intero non possono superare un certo limite prefissato. Questo porta a due importanti conseguenze: 1. Poichè il numero di cifre della mantissa m del numero a può essere illimitato, oppure finito ma superiore al numero di cifre t messe a 2.2. Rappresentazione dei numeri reali 21 disposizione nella rappresentazione del computer, solo un numero finito di numeri reali a possono essere esattamente rappresentati esattamente all’interno del computer. Tutti gli altri verranno rappresentati con una loro approssimazione a∗ . Il numero massimo t di cifre binarie, disponibile per la mantissa m, definisce la precisione del numero. 2. Il rango dei numeri reali normalizzati rappresentabili, presi in valore assoluto, è compreso tra un valore minimo amin = bL−1 ed un valore massimo amax = bU (1 − b−t ), dove L < 0 è il minimo esponente rappresentabile e U > 0 è il massimo esponente rappresentabile. Quindi i numeri con |a| < amin non sono rappresentabili in forma normalizzata e si ha una condizione di underflow; il numero a in tal caso viene trattato come zero oppure, se possibile, memorizzato come numero denormalizzato (anche detto subnormalizzato) ed in tal caso si parla di gradual underflow. I numeri con |a| > amax danno luogo al cosiddetto overflow; il numero a in tal caso assume il valore della rappresentazione al calcolatore dell’infinito (+Inf oppure -Inf). Pertanto solo un sottoinsieme dell’insieme R dei reali può essere rappresentato in un computer. Tale sottoinsieme viene chiamato insieme dei numeri floating point (o numeri macchina, o numeri in virgola mobile) e, con la nostra ipotesi di normalizzazione, può essere definito in questo modo: ( ∗ e ∗ e F = {0}∪ a ∈ R : a = ±(0.a1 a2 a3 . . . at )b ×b = ±b t X i=1 −i ai b = ± m∗b ×be−t ) con b ≥ 2, 0 ≤ ai ≤ b − 1, a1 6= 0, L ≤ e ≤ U ed m∗b = a1 a2 a3 . . . at . Quindi all’interno di un computer noi memorizziamo solamente i numeri ± e m∗b con e a r cifre binarie e m∗b a t cifre binarie. Per memorizzare i numeri floating point, si usano usualmente 4 byte (semplice precisione) oppure 8 byte (doppia precisione). Su taluni computer e con certi linguaggi si possono chiedere anche 10 o 16 byte (precisione estesa) oppure 6 byte. Il numero di bit dedicati alle due parti (esponente e mantissa) possono variare ed anche il tipo di rappresentazione binaria dell’esponente intero relativo e. 22 Capitolo 2. Aritmetica del computer Con 4 byte (qualsiasi sia l’architettura del computer) si riescono a rappresentare almeno 7 cifre decimali significative. Con 8 byte, tale numero viene elevato a circa 16 cifre decimali. Gli intervalli [−amax , −amin ] e [amin , amax ] della retta reale possono essere molto grandi (se vi sono molte cifre binarie a disposizione per l’esponente e), ma i numeri di F (in valore assoluto) sono molto densi vicino ad amin e più radi verso amax . Ad esempio, con 4 byte, vi sono circa 8 388 607 numeri floating point tra 1.0 e 2.0 e solo circa 8 191 tra 1023.0 e 1024.0. Si veda nella figura 2.1 una schematizzazione della cosiddetta floating point real line. denormalizzati under flow overflow under flow overflow rango utilizzabile rango utilizzabile − amin − a max 0 a min amax zoom Figura 2.1: Floating point real line Utilizzando una notazione abituale che si ritrova in molti testi, dato un numero a ∈ R, indichiamo con fl(a) ∈ F la sua approssimazione che viene rappresentata all’interno del computer. È importante, a fronte di tale approssimazione, riuscire a dare una limitazione superiore all’errore commesso (che in tal caso prende in nome di errore di assegnazione). Utilizzando le relazioni viste nel capitolo precedente, e tenuto presente che oramai la quasi totalità dei computer lavora con l’arrotondamento, avremo: Errore assoluto: εa = |a − fl(a)| ≤ 1 × be−t . 2 Si ha quindi fl(a) = a + ε1 con |ε1 | = εa . 2.2. Rappresentazione dei numeri reali 23 Errore relativo: εr = 1 |a − fl(a)| ≤ × b1−t = eps. |a| 2 (2.1) La quantità eps che, come si vede, dipende unicamente dalla base b e dal numero di cifre t (e quindi non dal numero che si vuole rappresentare) viene chiamata precisione di macchina ed è una caratteristica dell’architettura del computer su cui si sta lavorando. Dipende naturalmente anche dal numero di byte utilizzati nella rappresentazione dei numeri macchina, in quanto tale valore influisce sul valore di t. Se la base utilizzata è 2, si ha eps = fl(2−t ). Il valore eps rappresenta il più piccolo numero macchina positivo tale che fl(1 + eps) > 1 Si ha anche fl(a) = a (1 + ε2 ) con |ε2 | = εr ≤ eps. Vediamo alcuni esempi di un programma dove viene letto un valore reale a, usando la semplice precisione, e poi viene immediatamente stampato a∗ = fl(a). I valori di εa ed εr vengono calcolati con un programma che usa la doppia precisione, utilizzando i valori a ed a∗ della tabella. a 123456789.0 1.23456789 × 1013 3.34567891 3.34567891 × 1010 0.1 0.01 0.001 a∗ 123456792.0 1.2345679 × 1013 3.34567881 3.34567895 × 1010 0.100000001 0.00999999978 0.00100000005 εa 3.0 1.0 × 105 1.0 × 10−7 4.0 × 102 1.0 × 10−9 2.2 × 10−10 5.0 × 10−11 εr 2.43 × 10−8 8.1 × 10−9 2.99 × 10−8 1.2 × 10−8 1.0 × 10−8 2.2 × 10−8 5.0 × 10−8 Dai risultati si evince che, sia la conversione in binario del numero decimale, sia il numero limitato t di cifre della mantissa binaria che si possono memorizzare, già in fase di assegnazione, non ci permettono di lavorare con l’esatto valore a, ma con una sua approssimazione che può presentare un elevato errore assoluto. Resta comunque valida, per εr , la relazione (2.1) (eps nel caso della semplice precisione vale all’incirca 1.2 × 10−7 ). 24 Capitolo 2. Aritmetica del computer La singola precisione può in molti casi essere insufficiente a garantire una sufficiente affidabilità dei calcoli (soprattutto nelle operazioni aritmetiche floating-point, come vedremo in seguito). In tal caso è sempre opportuno utilizzare almeno la doppia precisione, anche se ciò provoca un allungamento dei tempi di calcolo e comunque non ci garantisce completamente dell’esattezza dei calcoli (si veda la sezione 2.7). 2.3 Rappresentazione IEEE 754 Lo standard IEEE 754 per il calcolo in virgola mobile (per esteso, IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Std 7541985) è il più diffuso. Il formato di rappresentazione dei numeri floating point è più complesso di quanto è stato descritto nella sezione precedente. Inoltre, con tale standard, possono essere rappresentati sia lo zero positivo che lo zero negativo ed anche i seguenti valori speciali: infinito positivo (Inf o +Inf), infinito negativo (-Inf), ±NaN (Not a Number). Il NaN viene utilizzato per rappresentare un valore che non è un numero reale. Esso viene assegnato, ad esempio, come risultato di operazioni indeterminate quali ±0/± 0, ±∞/± ∞, ∞ − ∞ e ±∞ · ±0. La base per l’esponente è b = 2 e la normalizzazione viene fatta considerando un valore compreso tra 1 e 2, ovvero i numeri normalizzati hanno la forma a∗ = ±(1.a1 a2 a3 . . . at )2 × 2e = ±(1 + 0.a1 a2 a3 . . . at )2 × 2e . La cifra 1 a sinistra del punto di radice non viene rappresentata (hidden bit, o bit nascosto, o bit implicito) e la mantissa corrisponde al numero binario m∗ = a1 a2 a3 . . . at (t numero di bit disponibili). L’esponente intero relativo e viene rappresentato eccesso 2r−1 − 1, dove r è il numero di bit disponibili per l’esponente, al fine di non dover utilizzare un bit per memorizzare il segno dell’esponente. Viene quindi memorizzato il valore intero binario positivo e∗ = e + 2r−1 − 1, ovvero si ha e∗ ± ∗ m∗ con a∗ = ±(1.m∗ )2 × 2e −(2 −1) . Per quanto riguarda il rango dei numeri reali normalizzati, presi in valore assoluto, rappresentabili con tale standard, esso è compreso tra un minimo amin = 2L ed un massimo amax = 2U (2 − 2−t ), dove L < 0 è r−1 2.3. Rappresentazione IEEE 754 25 il minimo esponente rappresentabile e U > 0 è il massimo esponente rappresentabile. Il numero di bit dedicati alle varie parti (segno, esponente e mantissa) per la semplice e la doppia precisione sono i seguenti byte 4 8 bit segno 32 1 64 1 esponente r=8 r = 11 mantissa t = 23 t = 52 Il minimo esponente e = e∗ − (2r−1 − 1) corrisponderebbe ad una rappresentazione in eccesso con tutti gli r bit uguali allo zero, ovvero e = 0 − (2r−1 − 1) (in semplice precisione 0 − 127 = −127, ed in doppia precisione 0 − 1023 = −1023) ed il massimo esponente corrisponderebbe ad una rappresentazione in eccesso con tutti gli r bit uguali ad uno, ovvero e = 2r − 1 − (2r−1 − 1) (in semplice precisione 255 − 127 = 128, ed in doppia precisione 2047 − 1023 = 1024). In realtà tali particolari configurazioni assunte dall’esponente e∗ (tutti i bit uguali a zero oppure tutti i bit uguali ad uno) vengono riservate per indicare dei valori particolari (zero, Inf, NaN ed anche i numeri denormalizzati). Quindi, riassumendo, per i numeri normalizzati si ha byte bit 4 32 64 8 byte 4 8 L U −126 127 −1022 1023 bit amin amax −38 32 1.17549435 × 10 3.40282347 × 1038 −308 64 2.2250738585072014 × 10 1.7976931348623157 × 10308 e, per i valori particolari, ±0 ± tutti i bit uguali ad 0 tutti i bit uguali a 0 ±Inf ± tutti i bit uguali ad 1 tutti i bit uguali a 0 ±Nan ± tutti i bit uguali ad 1 almeno un bit diverso da 0 Restano da definire i numeri denormalizzati che corrispondono ad una rappresentazione del tipo ± tutti i bit uguali ad 0 m∗ qualsiasi non nullo 26 Capitolo 2. Aritmetica del computer Tali rappresentazioni corrispondono a dei numeri reali per i quali il bit implicito alla sinistra è uguale a zero (anzichè ad uno), e l’esponente è sempre L, ovvero ai numeri a∗ = ±(0.m∗ )2 × 2L che sono, in valore assoluto, minori di amin . Il numero reale denormalizzato più piccolo rappresentabile in valore assoluto diventa quindi 2L−t (in semplice precisione 1.40129846 × 10−45 ed, in doppia precisione, 4.9406564584124654 × 10−324 ). Si veda ancora la figura 2.1. Relativamente byte 4 8 alla bit 32 64 precisione di macchina si ha eps fl(2−23 ) ' 1.19209290 × 10−7 fl(2−52 ) ' 2.2204460492503131 × 10−16 Nell’agosto del 2008 è stata pubblicata una revisione dello standard IEEE 754-1985, chiamata IEEE 754-2008, che estende, completa e sostituisce la precedente versione. 2.4 Operazioni aritmetiche floating-point Come si è visto, quello che viene rappresentato all’interno di un computer è solo un sottoinsieme di R. Vediamo ora cosa accade alle operazioni aritmetiche elementari quando gli operandi sono numeri macchina. Indichiamo le operazioni macchina (per distinguerle dalle operazioni aritmetiche) con i simboli ⊕, ª, ⊗, ®. Anzitutto si ricordi che le operazioni vengono eseguite utilizzando apposite zone di memoria della ALU (Arithmetic and Logic Unit), dette accumulatori o registri. Se in memoria centrale vi sono a disposizione t cifre per la mantissa, negli accumulatori di norma ve ne è un numero superiore (talvolta anche il doppio) in modo da aumentare la precisione del calcolo. Ma anche questa accortezza, in certe condizioni, non può evitare che nel calcolo si producono degli errori, talvolta anche grossolani. Supponiamo di dover eseguire, utilizzando il computer, l’operazione x+y che indichiamo con x⊕y. Anzitutto quello che sarà memorizzato, per quanto riguarda gli operandi, è una loro approssimazione, ovvero fl(x) e fl(y). Inoltre, il risultato della somma tra fl(x) e fl(y) verrà ulteriormente approssimato in quanto nella restituzione del risultato, si ritornerà ad avere t cifre per la mantissa. Pertanto: 2.4. Operazioni aritmetiche floating-point 27 x ⊕ y = fl(fl(x) + fl(y)) = (fl(x) + fl(y))(1 + ε1 ) con |ε1 | ≤ eps. Questo significa che, oltre agli errori di assegnazione, ad ogni addizione macchina si introduce un ulteriore errore. Ciò è vero anche per le altre operazioni, ovvero si ha x ª y = fl(fl(x) − fl(y)) = (fl(x) − fl(y))(1 + ε2 ) x ⊗ y = fl(fl(x) × fl(y)) = (fl(x) × fl(y))(1 + ε3 ) x ® y = fl(fl(x)/fl(y)) = (fl(x) / fl(y))(1 + ε4 ) con |ε2 |, |ε3 |, |ε4 | ≤ eps. 2.4.1 Proprietà Le operazioni su F non godono di tutte le proprietà delle corrispondenti operazioni su R. L’unica proprietà che resta valida è la proprietà commutativa ovvero x⊕y = y⊕x x⊗y = y⊗x La proprietà associativa e la distributiva non sono più valide! Si ha infatti x ⊕ (y ⊕ z) x ⊗ (y ⊗ z) x ⊗ (y ⊕ z) x ⊗ (y ª z) 6 = 6= 6 = 6 = (x ⊕ y) ⊕ z (x ⊗ y) ⊗ z (x ⊗ y) ⊕ (x ⊗ z) (x ⊗ y) ª (x ⊗ z) Anche altre proprietà risultano non più valide (x ⊗ y) ® y = 6 x (x ® y) ⊗ y = 6 x (x ⊗ y) ® z = 6 (x ® z) ⊗ y Relazione anomala. Un’altra violazione importante è data dalla cosiddetta relazione anomala che sancisce la non unicità dell’elemento neutro dell’addizione e della sottrazione (ovvero dello zero). Tale relazione può essere cosı̀ espressa: Se |fl(y)| < eps, allora |fl(x)| x ⊕ y = fl(x) x ª y = fl(x). Ciò accade quando x ed y hanno ordini di grandezza molto diversi. 28 Capitolo 2. Aritmetica del computer 2 ((x+y)−x)/y 1.5 1 0.5 0 13 13.5 14 14.5 15 15.5 16 16.5 q Figura 2.2: Effetti dell’aritmetica del computer Vediamo un esempio: consideriamo la seguente espressione algebrica (x + y) − x y (2.2) che nell’insieme R fornisce 1.0 come risultato, qualsiasi sia y 6= 0. Se prendiamo x = 1.0 ed y < eps, utilizzando un computer otterremo 0, a causa della relazione anomala. Se applichiamo in (2.2) la proprietà commutativa prima e l’associativa poi, otteniamo la seguente espressione (anch’essa matematicamente uguale ad 1.0) y + (x − x) , y e con il computer otterremo, con gli stessi valori di x e di y, il risultato esatto 1. Ma il fenomeno è ben più complicato. Infatti, diamo a y dei valori vicini alla precisione macchina (supponendo di lavorare con 8 byte), e precisamente y = 10−q con q = 13.0 + k × 0.01 per k = 0, . . . , 350. Si ottengono per l’espressione (2.2) i risultati della figura 2.2. Si vede che, 2.4. Operazioni aritmetiche floating-point 29 a seconda del valore di q, il risultato dell’espressione varia nell’intervallo [0, 2]. I valori di y sono decrescenti e, a partire da quando si realizza la relazione anomala, si ottiene sempre zero. La non validità della proprietà associativa assume particolare rilevanza quando si opera con numeri vicini ad amin oppure a amax . Vediamo, ad esempio, cosa accade alle seguenti espressioni algebriche equivalenti quando assegnamo ad x, y e a z, valori elevati come ordine di grandezza (utilizzando 4 byte e quindi con amax dell’ordine di 1038 ): x× x×y y = z z Sia x ' 1030 , y ' 1030 , z ' 1025 . Con la prima formula otteniamo un risultato numerico dell’ordine di 1035 , mentre con la seconda abbiamo una condizione di overflow ed otteniamo Inf. Analogamente assegnamo dei valori aventi ordine di grandezza molto piccolo (amin è dell’ordine di 10−38 ). Sia x ' 10−30 , y ' 10−30 , z ' 10−25 . Con la prima formula otteniamo un risultato numerico dell’ordine di 10−35 , mentre con la seconda abbiamo una condizione di underflow ed otteniamo 0. 2.4.2 Errori Consideriamo ora l’errore relativo, e cambiamo leggermente notazione. Indichiamo |x − fl(x)| εr (x) = |x| per ogni x ∈ R. Sia |(x + y) − (x ⊕ y)| ε⊕ , con x, y ∈ R, r (x, y) = |x + y| l’errore commesso nel calcolo della somma di due numeri. In modo simi⊗ ® lare si possono definire εª r (x, y), εr (x, y), εr (x, y). È possibile dimostrare che solamente le operazioni macchina ⊗ e ® sono stabili rispetto agli errori di arrotondamento, ovvero se εr (x) e εr (y) sono ® piccoli, allora lo sono anche ε⊗ r (x, y) ed εr (x, y). Un’operazione macchina si dice stabile quando ∃ c > 0, tale che ε¯ r (x, y) ≤ c (εr (x) + εr (y)) , ∀x, y ∈ R dove ¯ rappresenta una qualsiasi operazione macchina. 30 Capitolo 2. Aritmetica del computer Vediamo cosa accade quando deve essere eseguita una moltiplicazione (o una divisione). Si debba eseguire z = x × y (oppure z = x / y), con x, y, z ∈ R. Anzitutto i due valori reali a seguito dell’assegnazione in memoria, divengono fl(x) e fl(y). Poi il computer trasferisce dalla memoria agli accumulatori i due valori fl(x) ed fl(y) senza modificare la normalizzazione ma solamente aggiungendo degli zeri alla destra per completare i bit addizionali a disposizione degli accumulatori. Successivamente viene effettuata l’operazione tra le mantisse e separatamente viene calcolato l’esponente del risultato. Il risultato viene poi trasferito nuovamente in memoria, effettuando la normalizzazione e l’arrotondamento necessari per ricondursi a t cifre di mantissa. Di seguito riportiamo alcuni esempi, supponendo di lavorare (solo a scopo esplicativo) in un sistema a base decimale (con la base binaria il discorso sarà analogo), con t = 8 cifre decimali a disposizione per la mantissa in memoria e 2t = 16 cifre decimali a disposizione per la mantissa negli accumulatori. Esempio 1 Sia x = 0.27531012 × 10−2 ed y = 0.35720021 × 104 . Memoria fl(x) = 0.27531012 × 10−2 fl(y) = 0.35720021 × 104 fl(z) = 0.98340833 × 101 Accumulatore → 0.2753101200000000 × 10−2 → 0.3572002100000000 × 104 ← 0.0983408326791252 × 102 × Con tali valori si ha x = fl(x) ed y = fl(y). Nel caso di una moltiplicazione, il prodotto di due mantisse di lunghezza massima t fornisce un risultato di lunghezza al massimo 2t. Pertanto il risultato è esatto nell’accumulatore. L’unico errore che viene commesso è quello di assegnazione durante la restituzione in memoria quando il risultato viene normalizzato e ridotto a t cifre di mantissa. Esempio 2 Sia x = 0.57203146 × 10−1 ed y = 0.27001052 × 102 . Memoria fl(x) = 0.57203146 × 10−1 fl(y) = 0.27001052 × 102 fl(z) = 0.21185525 × 10−2 Accumulatore → 0.5720314600000000 × 10−1 → 0.2700105200000000 × 102 ← 2.118552491954754 × 10−3 / 2.4. Operazioni aritmetiche floating-point 31 Anche in questo esempio non vi è errore di assegnazione, ovvero x = fl(x) ed y = fl(y). Nel caso della divisione, il risultato nell’accumulatore non è sempre esatto (in quanto possono prodursi più di 2t cifre). Vi è quindi un errore nel risultato dell’accumulatore in quanto sarà arrotondato a 2t cifre. Nella restituzione in memoria l’errore di assegnazione sarà almeno nella t-esima cifra, a causa dell’arrotondamento e della normalizzazione. Errore di cancellazione. Gli esempi mostrano che le moltiplicazioni e le divisioni macchina non producono errori molto elevati. Purtroppo ciò non è sempre vero per le operazioni macchina ⊕ e ª. In talune condizioni si può realizzare uno degli errori più deleteri, chiamato l’errore di cancellazione. Esso si verifica quando si sommano tra loro numeri quasi uguali in modulo (ma di segno diverso) oppure quando si sottraggono numeri quasi uguali tra loro. È possibile infatti dimostrare che ε⊕ r (x, y) ¶ µ |y| |x| + εr (y) ≤ eps + (1 + eps) εr (x) |x + y| |x + y| con x, y ∈ R. Pertanto, quando y ' −x, il termine a destra (la maggiorazione) può essere anche molto grande e può permettere ad ε⊕ r (x, y) di essere molto elevato. Un discorso analogo vale per la sottrazione ª. Cerchiamo ora di descrivere cosa accade quando deve essere eseguita una somma (o una differenza) tra due numeri in modo da capire perchè tale operazione può essere cosı̀ imprecisa. Si debba eseguire z = x + y. Il computer trasferisce dalla memoria agli accumulatori i due valori fl(x) ed fl(y) senza modificare quello tra i due operandi che risulta essere il maggiore in valore assoluto, e modificando l’altro operando in modo che abbia lo stesso esponente di quello maggiore in valore assoluto (vengono quindi eventualmente aggiunti degli zeri davanti alla prima cifra a1 ). Anche in tal caso, in tale trasferimento, vengono aggiunti degli zeri alla destra per completare i bit addizionali a disposizione degli accumulatori. Successivamente viene effettuata l’operazione ed il risultato viene poi trasferito nuovamente in memoria, effettuando la normalizzazione e l’arrotondamento necessari per ricondursi a t cifre di mantissa. Riportiamo alcuni esempi supponendo ancora di lavorare in un sistema a base decimale, con t = 8 cifre decimali per la mantissa in memoria e 2t = 16 cifre decimali per la mantissa negli accumulatori. Per un sistema di questo tipo, utilizzando la formula (2.1), si ha eps = 0.5 × 10−7 . 32 Capitolo 2. Aritmetica del computer Esempio 1 Sia x = 0.23487757 × 103 ed y = 0.56799442. Memoria fl(x) = 0.23487757 × 103 fl(y) = 0.56799442 × 100 fl(z) = 0.23544556 × 103 → → ← Accumulatore 0.2348775700000000 × 103 0.0005679944200000 × 103 0.2354455644200000 × 103 + Abbiamo x = fl(x) ed y = fl(y). Per quanto riguarda l’addizione, essa viene eseguita nell’accumulatore in modo esatto (ovvero conservando esattamente i valori fl(x) ed fl(y)) e l’unico errore introdotto risulta essere quello di assegnazione durante la restituzione del risultato in memoria (poichè dobbiamo conservare solo t = 8 cifre della mantissa). Esempio 2 Sia x = 0.56543451 × 106 ed y = 0.21554623 × 10−4 . Memoria fl(x) = 0.56543451 × 106 fl(y) = 0.21554623 × 10−4 fl(z) = 0.56543451 × 106 → → ← Accumulatore 0.5654345100000000 × 106 0.0000000000215546 × 106 0.5654345100215546 × 106 + In questo esempio, poichè la grandezza dei due operandi è abbastanza diversa (sono distanti tra loro) il trasferimento di fl(y) nell’accumulatore produce un errore (si perdono due cifre). Pertanto la somma calcolata nell’accumulatore non sarà esatta (ovvero non verrà calcolato esattamente fl(x) + fl(y)). Inoltre, restituendo il risultato in memoria (poichè dobbiamo conservare solo t = 8 cifre della mantissa) si ottiene fl(z) = fl(x), ovvero si realizza la relazione anomala. Infatti |fl(y)| / |fl(x)| è dell’ordine di 10−10 e quindi minore di eps. Esempio 3 Sia x = 1.0 ed y = 0.5 × 10−7 . Memoria fl(x) = 0.10000000 × 101 fl(y) = 0.50000000 × 10−7 fl(z) = 0.10000001 × 101 → → ← Accumulatore 0.1000000000000000 × 101 0.0000000050000000 × 101 0.1000000050000000 × 101 + In questo esempio abbiamo x = fl(x) = 1 e y = fl(y) = eps. Si vede che la somma viene eseguita correttamente negli accumulatori e, nella resti- 2.4. Operazioni aritmetiche floating-point 33 tuzione del risultato in memoria, a causa dell’arrotondamento si ottiene fl(z) = 1.0000001. Se avessimo utilizzato per y il valore 0.4 × 10−7 si sarebbe realizzata la relazione anomala. Dunque eps è effettivamente il più piccolo valore per il quale fl(1 + eps) > 1. Esempio 4 Sia x = 0.5654328749876 ed y = −0.5654328510104. Memoria fl(x) = 0.56543287 × 100 fl(y) = −0.56543285 × 100 fl(z) = 0.20000000 × 10−7 Accumulatore → 0.5654328700000000 × 100 → −0.5654328500000000 × 100 ← 0.0000000200000000 × 100 + In questo esempio, abbiamo espressamente inserito del valori x 6= fl(x) ed y 6= fl(y), per enfatizzare quanto accade. L’addizione negli accumulatori viene eseguita in modo esatto (ovvero conservando esattamente i valori fl(x) ed fl(y)). Tuttavia, nella restituzione del risultato in memoria, gli zeri che seguono la cifra 2 non hanno alcun significato! Essi provengono solo dal fatto che negli accumulatori abbiamo aggiunto degli zeri alla destra della mantissa. Gli zeri del risultato non hanno significato come non ne avrebbe avuto qualsiasi altra cifra decimale che avessimo messo al loro posto. Infatti, la somma x + y darebbe 0.239772 × 10−7 e quindi l’errore relativo (approssimato a 3 cifre decimali) è εr (z) = 0.166, che è elevato. Nei calcoli successivi, utilizzando fl(z) (anzichè z) è come se lavorassimo con solo 1 cifra decimale esatta. È l’errore di cancellazione. Regola per l’errore di cancellazione. Siano mx ed my le mantisse di due numeri x ed y aventi lo stesso segno (rispettivamente segno opposto) che hanno coincidenti le prime r cifre. La differenza (rispettivamente la somma) tra questi due numeri ha solamente le prime (t − r) cifre del risultato che possono considerarsi significative (t rappresenta il numero di cifre significative che possono essere rappresentate in memoria). Tutte le altre cifre non hanno alcun significato. Diamo ora alcuni esempi utilizzando la semplice e la doppia precisione per mettere in luce come i calcoli in semplice precisione siano affetti da errori anche rilevanti. Questi risultati sono stati ottenuti su di un computer, mentre gli esempi precedenti, svolti a mano, servivano unicamente a mostrare come vengono effettuate le operazioni. 34 Capitolo 2. Aritmetica del computer x 123456789.0 123456789.0 0.56543451×106 1.0 0.5654328749876 0.3333333333 y 123456788.0 123456790.0 0.21554623×10−4 0.5 × 10−6 0.5654328510104 0.1111111111 semplice precisione xªy x⊕y 8.0 246913568.0 0.0 246913584.0 565434.5 565434.5 0.999999523 1.00000048 0.0 1.13086569 0.222222239 0.444444448 doppia precisione x y xªy x⊕y 123456789.0 123456788.0 1.0 246913577.0 123456789.0 123456790.0 −1.0 246913579.0 565434.510022 0.56543451×106 0.21554623×10−4 565434.509978 1.0 0.5 × 10−6 0.9999995 1.0000005 −7 1.130865726 0.5654328749876 0.5654328510104 0.239772000032×10 0.3333333333 0.1111111111 0.2222222222 0.4444444444 2.4.3 Esempi sull’errore di cancellazione Talvolta è possibile modificare le proprie espressioni in modo da cautelarsi nei confronti di un possibile errore di cancellazione. Semplici trasformazioni di tipo algebrico o modifiche nell’algoritmo possono aiutarci ad evitare tale errore. Vediamo alcuni esempi. Calcolo delle radici di un polinomio di secondo grado A tutti è noto che le radici del polinomio ax2 + bx + c = 0 sono date da √ −b − b2 − 4ac x1 = √2a (2.3) −b + b2 − 4ac . x2 = 2a Se |4ac| << b2 una delle due soluzioni sarà mal calcolata, in quanto √ b2 − 4ac ' |b| e quindi si verificherà un errore di cancellazione a causa della differenza di due numeri quasi uguali. Ad esempio, se a = 10−3 , b = 0.8 et c = −1.2 × 10−5 , le soluzioni esatte sono x1 = −800 x2 = 1.5 × 10−5 . 2.4. Operazioni aritmetiche floating-point 35 La prima soluzione, fl(x1 ), su di un computer, è ben calcolata mentre per la seconda si ha fl(x2 ) = 2.980232 × 10−5 , con εr (x2 ) ' 0.9868. Per evitare tale errore, utilizziamo il fatto che, data una soluzione x1 della nostra equazione, la seconda soluzione può essere calcolata come x2 = c . ax1 (2.4) Pertanto, sapendo che la soluzione ben calcolata è quella che evita l’eventuale errore di cancellazione, ovvero è sempre data da √ −b − sign(b) b2 − 4ac (2.5) x1 = 2a dove sign(b) rappresenta il segno del numero b, anzichè utilizzare le formule (2.3), sarà meglio usare le formule (2.4) e (2.5). Vediamo nella figura 2.3 i risultati ottenuti per la soluzione x2 (quella che può essere mal calcolata) con le formule (2.3) (linea continua) e quelli ottenuti con le formule (2.4, 2.5) (linea tratteggiata) nel caso in cui si abbia a = 10−3 , b = 0.8 et c = −(1 + k × 0.05) × 10−5 per k = 0, . . . , 100. x10 -5 9 8 7 6 x_2 5 4 3 2 1 0 -6 -5.5 -5 -4.5 -4 -3.5 -3 -2.5 -2 -1.5 valore di c Figura 2.3: Calcolo degli zeri di ax2 + bx + c -1 x10 -5 36 Capitolo 2. Aritmetica del computer Formule alternative Vediamo ora alcuni esempi di espressioni che potenzialmente potrebbero essere affette dall’errore di cancellazione. Semplici modifiche di tipo algebrico, ci permettono di considerare delle espressioni equivalenti che non presentano tale problema. Esempio 1 Sia |δ| << |x|. √ x+δ− √ x= √ δ √ x+δ+ x Esempio 2 Sia |δ| << |x|. ¶ µ δ δ cos(x + δ) − cos(x) = −2 sin sin x + 2 2 Esempio 3 Sia |x| grande. 2.5 1 1 1 − = x x+1 x(x + 1) Stabilità e condizionamento In questa sezione cercheremo di definire ed illustrare due nozioni fondamentali del calcolo numerico: il condizionamento di un modello matematico (o numerico) e la stabilità numerica di un algoritmo. Sono due concetti che non vanno confusi tra loro. Supponiamo di avere un certo problema (modello) matematico (o numerico) e che una piccola variazione dei dati numerici di ingresso (detta perturbazione) di tale modello, porti ad una soluzione molto lontana da quella del problema non perturbato. Diamo un esempio molto semplice: Sia dato il seguente sistema lineare a due incognite ½ 2.1x + 3.5y = 8.0 4.19x + 7.0y = 15.0 2.5. Stabilità e condizionamento 37 che ammette per soluzioni x = 100.0 y = −57.71428571 . . . Aggiungiamo una piccola perturbazione al coefficiente della x nella seconda equazione, ovvero al posto di 4.19 scriviamo 4.192. L’errore relativo dovuto a tale perturbazione è all’incirca 4.77 × 10−4 . Se risolviamo ora il sistema perturbato otteniamo come soluzioni x = 125.0 y = −72.71428571 . . . con un errore relativo su x di 0.25 e su y di circa 0.26, quindi elevati. Come si è visto tale problematica non dipende dall’algoritmo utilizzato ma dal problema in se stesso (trattandosi geometricamente di due rette quasi parallele, è naturale che una piccola variazione di inclinazione di una delle due rette, provochi uno spostamento notevole del punto di intersezione). Tenendo presente che, utilizzando un computer, abbiamo visto che anche solo a causa dell’errore di assegnazione non si lavora con i dati esatti, ma con una loro approssimazione, la risoluzione del sistema ci porterà ad avere soluzioni anche molto diverse dalla soluzione esatta (e questo a prescindere dell’algoritmo utilizzato). Possiamo quindi dare la seguente definizione Condizionamento: Un problema si dice ben condizionato se a piccole perturbazioni (errori relativi) sui dati di ingresso, corrispondono perturbazioni (errori relativi) sui risultati piccole (ovvero dello stesso ordine di grandezza). Un problema mal condizionato invece amplifica fortemente gli errori relativi dai dati ai risultati (non necessariamente tutti i risultati). Numero di condizionamento: Sia P un problema i cui dati siano d ed il risultato r. Supponiamo che una variazione ∆d dei dati provochi una variazione ∆r del risultato. È possibile definire il numero di condizionamento di un problema P (indicato con cond(P)) come una limitazione superiore del fattore di amplificazione degli errori relativi, ovvero k∆rk k∆dk ≤ cond(P) , krk kdk 38 Capitolo 2. Aritmetica del computer dove con k · k indichiamo una funzione (norma) che misura le entità coinvolte (si veda l’appendice, sezione A.7). Un altro esempio di problema mal condizionato è dato dalla ricerca delle radici di un polinomio quando vi sono radici α grandi e la derivata del polinomio calcolata in tali α è piccola, oppure quando vi sono due radici molto vicine oppure multiple. Un esempio classico di tale problema è dato dal Polinomio di Wilkinson P20 = (x + 1)(x + 2) · · · (x + 20) = x20 + 210x19 + · · · + 20! . Pur avendo radici ben separate (αi = −i, i = 1, . . . , 20), si riescono a calcolare bene le prime 15, ma non le ultime 5. Vediamo ora di definire il secondo concetto. Per risolvere un certo problema noi usiamo un certo algoritmo ed il programma corrispondente sul computer. Vi saranno quindi necessariamente degli errori dovuti all’aritmetica del computer che potranno propagarsi in modo più o meno rilevante sui risultati. Quindi un problema può essere per natura ben condizionato, ma l’algoritmo utilizzato per risolverlo può essere numericamente instabile. In tal caso, è necessario usare (o trovare) un algoritmo stabile. Un esempio di algoritmo instabile e di un algoritmo alternativo stabile (in quanto evita gli eventuali problemi di cancellazione) lo abbiamo visto nella sezione 2.4.3. Diamo una definizione di stabilità numerica di un algoritmo: Stabilità: Un algoritmo si dice stabile se a piccole perturbazioni (errori relativi) sui dati, corrispondono perturbazioni (errori relativi) sui risultati dello stesso ordine di grandezza. Un algoritmo si dice instabile se gli errori piccoli iniziali (o presenti ad un certo momento) anzichè compensarsi mediamente (o mantenersi limitati) tendono a crescere in modo incontrollato sino a degradare completamente il risultato finale. Vediamo un esempio classico di due algoritmi, che risolvono lo stesso problema, l’uno stabile e l’altro instabile. Sia da calcolare l’integrale 1 In = e Z 0 1 xn ex dx con n ≥ 0 2.5. Stabilità e condizionamento 39 Integrando per parti si ha ¶ µ Z 1 1 n−1 x n x 1 x e dx = 1 − n In−1 [x e ]0 − n In = e 0 Z 1 1 x e−1 1 Poichè I0 = e dx = , si ha I1 = ed il seguente schema e 0 e e ricorsivo ( 1 I1 = e Ii = 1 − i Ii−1 , i = 2, . . . , n Z 1 1 È possibile dimostrare che, ∀i, 0 < Ii < xi dx = . Pertanto i+1 0 lim Ii = 0 ed i valori devono essere decrescenti in valore assoluto. i→∞ Dimostriamo ora che tale algoritmo per il calcolo dell’integrale In è instabile. Indichiamo con Ii il valore esatto e con I0i il valore approssimato al passo i-esimo. Si ha I0i = Ii + εi , con |εi | uguale all’errore assoluto al passo i. Analogamente I0i−1 = Ii−1 + εi−1 e quindi, dalla formula ricorsiva I0i = 1 − i I0i−1 , si ha Ii + εi = 1 − i (Ii−1 + εi−1 ) ovvero εi = −i εi−1 che è la relazione che lega l’errore al passo i-esimo, rispetto all’errore al passo (i − 1)-esimo. Calcoliamo ora quanto vale εn rispetto a ε1 . Si ha εn = −n (−(n − 1) εn−2 ) = · · · = −n (−(n − 1)) · · · (−2)ε1 = (−1)n−1 n! ε1 . Quindi, anche supposto che ε1 sia molto piccolo, e non tenendo conto degli errori di aritmetica, εn aumenta rapidamente (a causa del fattoriale) e si ha lim |εn | = ∞. n→∞ Vediamo, a conferma di tale analisi, alcuni risultati ottenuti utilizzando la semplice precisione (al fine rendere più visibile l’instabilità, che comunque si presenta anche in doppia precisione). Si ha I1 = e−1 e I01 = 3.6787945 × 10−1 . Utilizzando lo schema ricorsivo in avanti, otteniamo i risultati della seguente tabella. Essi mostrano come il valore I0n tenda ad aumentare rapidamente, anzichè convergere verso lo zero, ottenendo per I021 un numero dell’ordine di 1011 . In doppia precisione si ha I021 = −2.201076724843952 × 103 . 40 Capitolo 2. Aritmetica del computer I0n 2.6424110 × 10−1 2.0727670 × 10−1 1.7089319 × 10−1 1.4553404 × 10−1 1.2679577 × 10−1 1.1242962 × 10−1 1.0056305 × 10−1 9.4932556 × 10−2 5.0674438 × 10−2 4.4258118 × 10−1 n 2 3 4 5 6 7 8 9 10 11 n 12 13 14 15 16 17 18 19 20 21 I0n −4.3109741 × 100 5.7042664 × 101 −7.9759729 × 102 1.1964959 × 104 −1.9143834 × 105 3.2544528 × 106 −5.8580148 × 107 1.1130228 × 109 −2.2260457 × 1010 4.6746960 × 1011 Diamo ora l’algoritmo stabile (noto come algoritmo di Miller) che costruisce la famiglia di integrali in senso regressivo, ovvero facendo decrescere gli indici. Si fissa un indice N ∈ N (anche non troppo grande) con N > n, si pone IN uguale ad un valore arbitrario qualsiasi, oppure a zero (commettendo certamente un errore εN ) e si considera il seguente algoritmo ricorsivo ( IN = a 1 Ii−1 = (1 − Ii ) , i = N, N − 1, . . . , 2 i con a ∈ R arbitrario. Si ha I00i = Ii + εi , e I00i−1 = Ii−1 + εi−1 . Quindi, dalla formula ricorsiva, 1 Ii−1 + εi−1 = (1 − Ii − εi ) ovvero i 1 εi−1 = − εi i che è la relazione che lega l’errore per l’indice (i − 1), rispetto all’errore per l’indice i. Supponendo di aver commesso (assegnando il valore di partenza IN = a) un errore εN = I00N − IN , avremo .. . 1 εN N 1 (−1)2 = − εN −1 = εN N −1 N (N − 1) .. . ε1 = εN −1 = − εN −2 (−1)N −1 εN N! 2.6. Analisi degli errori 41 Quindi il fattoriale di N al denominatore abbatte rapidamente l’errore iniziale εN , che può anche essere elevato. Sia ad esempio N = 13 ed I13 = 1000. Si ottiene i I00i 12 −7.6846153 × 101 11 6.4871793 × 100 10 −4.9883449 × 10−1 9 1.4988345 × 10−1 8 9.4457395 × 10−2 7 1.1319283 × 10−1 i 6 5 4 3 2 1 I00i 1.2668674 × 10−1 1.4555222 × 10−1 1.7088956 × 10−1 2.0727761 × 10−1 2.6424080 × 10−1 3.6787960 × 10−1 con |I01 − I001 | = 1.4901161 × 10−7 (I01 è l’approssimazione del valore esatto I1 = e−1 ). Come si vede, pur partendo da un valore iniziale completamente arbitrario, l’integrale I001 (approssimazione di I1 ) risulta essere accurato per almeno 6 cifre significative decimali. Qualora l’errore |I01 − I001 | fosse stato troppo elevato, ovvero maggiore di una certa tolleranza toll prefissata, si aumenta il valore di N e si ripete il procedimento sino ad ottenere la precisione desiderata. Naturalmente i primi integrali calcolati, rispetto agli integrali esatti, saranno affetti da un errore non trascurabile. Nella tabella precedente si vede, ad esempio, che l’effetto di diminuzione dell’errore inizia solamente nel calcolo di I008 . Ripetiamo il procedimento prendendo N = 20 ed ancora I20 = 1000 e mostrando solo gli integrali a partire da I0012 . i 12 11 10 9 8 7 I00i 7.1773447 × 10−2 7.7352211 × 10−2 8.3877072 × 10−2 9.1612294 × 10−2 1.0093196 × 10−1 1.1238351 × 10−1 i 6 5 4 3 2 1 I00i 1.2680236 × 10−1 1.4553294 × 10−1 1.7089342 × 10−1 2.0727664 × 10−1 2.6424113 × 10−1 3.6787945 × 10−1 In tal caso, I001 coincide esattamente con I01 . 2.6 Analisi degli errori Abbiamo visto quante problematiche possono essere presenti quando vogliamo risolvere un problema matematico utilizzando un computer 42 Capitolo 2. Aritmetica del computer (condizionamento, errori di assegnazione, dovuti alle operazioni macchina, stabilità dell’algoritmo, . . . ). A tutte queste si aggiunge anche il fatto che, per produrre il modello numerico di un problema matematico, siamo talvolta obbligati a ricorrere a metodi numerici che forniscono solo una soluzione approssimata del problema matematico (ad esempio nell’integrazione finita). Quando possibile, può essere interessante studiare la propagazione degli errori. È quanto in effetti è stato fatto nell’esempio dell’integrale della sezione precedente. Non intendiamo approfondire tale argomento, ma molti semplici esempi possono essere trovati nei vari testi della letteratura. Quando si studia come l’errore si propaga per effetto dell’errore di assegnazione e delle successive operazioni, si parla di analisi dell’errore in avanti. Nei casi reali tale analisi risulta difficilmente realizzabile (a causa soprattutto del numero importante delle operazioni che vengono svolte). Inoltre si effettuano delle semplificazioni dei calcoli che conducono spesso a delle stime poco accurate dell’errore finale. Una tecnica alternativa, molto usata, è quella dell’analisi dell’errore all’indietro. In tale caso si considera la soluzione approssimata ottenuta, come se fosse soluzione esatta di un problema P che sia modificato rispetto al problema iniziale P. Poi si studia quanto sia grande la modifica del problema iniziale al fine di ottenere tale soluzione. Il risultato finale ottenuto sarà accettabile solo se esso è soluzione esatta di un problema modificato che possa essere considerato prossimo a quello originale. In termini di errori, si considera quale dovrebbe essere l’errore sui dati iniziali originari per produrre tale risultato finale. Terminiamo con alcune definizioni che riguardano gli errori: Sia ε0 l’errore iniziale, ed εn l’errore dopo n passi di un certo algoritmo. La crescita dell’errore è detta lineare se εn ' n ε0 esponenziale se εn ' cn ε0 Se c > 1 l’errore cresce (εn → ∞, per n → ∞) Se 0 < c < 1 l’errore descresce (εn → 0, per n → ∞) 2.7. Esempi 2.7 43 Esempi In questa sezione vogliamo fornire alcuni esempi tratti dalla letteratura per illustrare maggiormente le varie problematiche, soprattutto quelle relative alla precisione finita dell’aritmetica del computer. 2.7.1 Esempio 1 Spesso si ritiene che se un risultato calcolato con precisioni diverse resta invariato, è obbligatoriamente ben calcolato. Questo esempio mostra che tale convinzione è errata. Sia da calcolare il valore di a (2.6) 333.75b6 + a2 (11a2 b2 − b6 − 121b4 − 2) + 5.5b8 + . 2b Per a2 − 1 = 11b2 /2 l’espressione vale analiticamente −2 + a/2b. Consideriamo i valori a = 77617.0 e b = 33096.0 tali che a2 − 1 = 11b2 /2. Calcolando numericamente l’espressione (2.6) si ottiene Semplice precisione Doppia precisione Precisione estesa 0.5764607×1018 0.5764607523034235×1018 0.57646075230342350345×1018 Tuttavia il risultato con tali valori dovrebbe essere −2 + a/2b = −0.82739605994682 . . . I risultati in tale problema dipendono fortemente anche dal computer su cui si lavora e dal modo in cui si programma la formula. Infatti, su di un altro computer si è ottenuto circa −9.87 × 1029 in semplice precisione, e circa −1.18 × 1021 in doppia precisione! 2.7.2 Esempio 2 Consideriamo il polinomio x3 − 111x2 + 1130x − 3000. Le sue radici sono 5, 6 e 100. È possibile dimostrare che la successione xn+1 = 111 − 3000 1130 + , xn xn xn−1 n = 0, 1, . . . converge a 6 se si prendono come valori iniziali x−1 = 11 61 = 5.5 e x0 = = 5.54 2 11 44 Capitolo 2. Aritmetica del computer 200 100 0 x n −100 −200 −300 −400 −500 −600 0 5 10 15 20 25 n Figura 2.4: xn+1 = 111 − 1130 3000 + xn xn xn−1 In figura 2.4 si vede invece che, utilizzando un computer, la successione converge al valore 100 anzichè a 6. Questo fenomeno sembra prodursi su tutti i computer. Infatti, ad esempio, se si prendono come valori iniziali x−1 = 2.0 e x0 = −4.0 facendo i calcoli con 100 cifre decimali si trova x89 = 99.999804031654181616554 . . . mentre il valore esatto dovrebbe essere 6.0000000996842706 . . .. 2.7.3 Esempio 3 Consideriamo la seguente successione che converge al valore x = 1 x0 = 1.510005072136258 3x4n − 20x3n + 35x2n − 24 , xn+1 = 4x3n − 30x2n + 70xn − 50 n = 0, 1, . . . A seconda del numero di cifre decimali k utilizzate nei calcoli, si ottengono risultati molto diversi. 2.7. Esempi 45 k 10 12 14 16 18 2.7.4 Valore di convergenza 4.000000033 1.00000000000 3.0000000000000 4.000000000000033 1.00000000000000000 Esempio 4 Si consideri il seguente schema iterativo x0 passegnato 1 − x20 y0 = t0 = x20 + y02 = 1 xn+1 = 2xn yn yn+1 = x2n − yn2 2 tn+1 = x2n+1 + yn+1 per n = 0, 1, . . . Matematicamente si ha tn+1 = (x2n + yn2 )2 = t2n = 1 (per induzione, essendo t0 = 1). In figura 2.5, si sono visualizzate le curve ottenute su di un computer, nei due casi: x0 = 0.6 x0 = 0.8 tn linea continua tn linea tratteggiata Come si vede, dopo essersi mantenuto uguale a 1, nel primo caso tn cresce, mentre nel secondo tende a zero. 2.7.5 Esempio 5 Consideriamo la successione {xn } calcolata come xn+1 = 2.25 xn − 0.5 xn−1 , n = 2, 3, . . . (2.7) con x1 e x2 assegnati. La teoria delle equazioni alle differenze lineari omogenee, a coefficienti costanti, ci dice che xn = a 0.25n + b 2n , n = 1, 2, . . . (2.8) dove a e b sono delle costanti i cui valori sono determinati da x1 e x2 . Se noi prendiamo x1 = 1/3 e x2 = 1/12, si ha a = 4/3 et b = 0. Di 46 Capitolo 2. Aritmetica del computer 2.4 2.2 2 1.8 t_n 1.6 1.4 1.2 1 0.8 0.6 0 5 10 15 20 25 n Figura 2.5: tn+1 = (x2n + yn2 )2 = t2n 10 9 10 6 10 3 10 0 10 -3 10 -6 10 -9 10 -12 0 10 20 30 40 50 Figura 2.6: xn+1 = 2.25xn − 0.5xn−1 60 70 2.8. Algoritmi 47 conseguenza da (2.8) si ha xn = 41−n /3 e tale quantità tende a zero quando n tende verso l’infinito. I risultati ottenuti su computer, utilizzando la formula (2.7) sono visualizzati in figura 2.6. Cerchiamo di capire cosa è accaduto. A causa degli errori di assegnazione iniziali su x1 e x2 , il valore b implicitamente definito da (2.7) non è esattamente nullo, ma solo molto piccolo. Nella prima parte della curva, l’apporto del termine b 2n è trascurabile, ma diviene preponderante nella parte destra della curva. 2.8 Algoritmi 2.8.1 Stima di eps Un semplice algoritmo (dovuto a Cleve Moler) per la stima del valore eps è il seguente: x = 4.0/3.0 y = x − 1.0 z =y+y+y eps = |z − 1.0| 2.8.2 Calcolo della base Abbiamo già detto che non tutti i computer lavorano con normalizzazione nella base 2. Se si lavora con computer che utilizzano basi diverse, non è inusuale che i risultati di uno stesso programma risultino essere diversi (anche di molto) su computer differenti. Un semplice algoritmo per il calcolo della base utilizzata da un computer è il seguente: a = 1.0 b = 1.0 while ((a + 1.0) − a) − 1.0 = 0.0 do a = 2.0 × a end while while ((a + b) − a) − b 6= 0.0 do b = b + 1.0 end while print b Il valore di b rappresenta la base interna utilizzata dal computer. 48 Capitolo 2. Aritmetica del computer 2.8.3 Equazione di secondo grado Si vuole un algoritmo che permetta di risolvere un’equazione di secondo grado, i cui coefficienti reali sono indicati con a, b e c, effettuando tutti i possibili controlli. Nel caso di due soluzioni reali distinte, l’algoritmo utilizza la formula usuale. Come si è detto (vedi sezione 2.4.3), per evitare eventuali errori di cancellazione, è preferibile utilizzare le formule alternative. L’algoritmo proposto può essere facilmente modificato in tal senso come esercizio. [x1 , x2 ] = Eq grado2 (a, b, c) if a = 0 then if b = 0 then if c = 0 then print Equazione indeterminata else print Equazione impossibile end if else x1 = −c/b print Equazione di grado 1 (unica soluzione x1 ) end if else ∆ = b2 − 4ac if ∆ < 0 then print Nessuna soluzione reale else if ∆ = 0 then print Due soluzioni coincidenti (x1 = x2 ) x1 = −b/(2a) x2 = x1 else print Due soluzioni distinte (x1 6= x2 ) √ x1 = (−b − √∆)/(2a) x2 = (−b + ∆)/(2a) end if end if 2.9. Esercizi 2.9 49 Esercizi Esercizio 2.1 Si scriva un algoritmo ed il relativo programma che, dati amin e amax , calcoli il primo numero positivo a ∈ F che sia maggiore di amin , ed il primo numero positivo a ∈ F che sia minore di amax , sia in semplice che in doppia precisione.. Esercizio 2.2 Si deve eseguire la somma di n numeri reali x1 , . . . xn di cui non si conoscono a priori nè i segni nè l’ordine di grandezza. Se si lavorasse con precisione infinita un possibile algoritmo sarebbe for i = 1, . . . , n read xi end for somma = x1 for i = 2, . . . , n somma = somma + xi end for print somma Si scriva un algoritmo che esegua la somma in modo da evitare i possibili errori dovuti all’aritmetica del computer. Si realizzi poi il programma che calcola la somma nei due modi, e si confrontino i risultati. Esercizio 2.3 Si scriva un algoritmo per il calcolo del valore reale della seguente espressione (algebricamente uguale ad 1 per ogni valore di n), senza effettuare alcuna semplificazione: Ãs n−1 n − (n − 2) n − (n − 1) n × × ··· × × n−1 n−2 n − (n − 1) 1 × !2 n − (n − 1) 1 n−1 n−2 × × ··· × × . n n−1 n − (n − 2) n − (n − 1) Si realizzi poi un programma che effettui tale calcolo per n = 2, . . . , 100, sia in semplice che in doppia precisione, calcolando anche, per ogni n, l’errore assoluto, l’errore relativo ed il numero di cifre significative esatte. Si analizzino poi i risultati trovati e si dica quali di essi sono affetti da errore dovuto all’aritmetica del computer e quali sono le possibili ragioni. 50 Capitolo 2. Aritmetica del computer Esercizio 2.4 Si realizzi un programma che, utilizzando due funzioni corrispondenti ai due algoritmi di calcolo (diretto ed Horner), calcoli il valore di un polinomio P (x) in un punto x̄ (si veda la sezione 1.2.3). Si confrontino i due risultati ottenuti al variare di x̄ ed al variare dei coefficienti del polinomio P (x). Esercizio 2.5 Modificando opportunamente l’algoritmo proposto nella sezione 2.8.3, si introducano le formule stabili per la determinazione delle soluzioni reali di un’equazione di secondo grado a coefficienti reali (vedi sezione 2.4.3). Si scrivano poi i relativi programmi e si ritrovino i risultati della Figura 2.3. Esercizio 2.6 Prendendo spunto dall’algoritmo proposto nella sezione 2.8.3, si scriva un algoritmo per il calcolo delle soluzioni (reali e/o complesse) di un’equazione di secondo grado a coefficienti reali.