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.