Capitolo 3
Aritmetica in virgola mobile
3.1 Le operazioni base dell’aritmetica in virgola mobile
La seguente tabella riassume le operazioni di base per l’aritmetica in virgola mobile:
Numeri in virgola mobile Operazioni aritmetiche
X E X B M X = ⋅ Y E Y B M Y = ⋅ Y x E Y E E X E Y E E X E E B M B M Y X B M B M Y X Y Y X Y Y X ≤ ⋅ − ⋅ = − ⋅ + ⋅ = + − − ) ( ) ( Y X E E Y X M B M Y X ⋅ =( + )⋅ + Y X E E Y X B M M Y X ⋅ − =
Esempi (in base 10): 30 10 3 , 0 ⋅ 2 = = X 200 10 2 , 0 ⋅ 3 = = Y 230 10 23 , 0 10 ) 2 , 0 10 3 , 0 ( ⋅ 2 3 + ⋅ 3 = ⋅ 3 = = +Y − X 170 10 ) 17 , 0 ( 10 ) 2 , 0 10 3 , 0 ( ⋅ 2 3 − ⋅ 3 = − ⋅ 3 =− = −Y − X 5 3 2+ 0,06 10 6000 10 ) 2 , 0 3 , 0 ( ⋅ ⋅ = ⋅ = = ⋅ Y X 1 3 2− 1,5 10− 0,15 10 ) 2 , 0 3 , 0 ( ÷ ⋅ = ⋅ = = ÷ Y X
Tabella 3.1 – Operazioni di base per numeri rappresentati in virgola mobile
Nel caso dell’addizione e della sottrazione, è necessario assicurarsi che entrambi gli operandi abbiano lo stesso valore all’esponente e ciò può richiedere lo scorrimento della virgola su uno degli operandi per raggiungere l’allineamento. La moltiplicazione e la divisione sono più semplici.
I problemi che possono sorgere eseguendo queste operazioni sono: • Owerflow
dell’esponente :
un esponente positivo supera il massimo valore permesso. In alcuni sistemi questo evento è indicato come +∞ oppure −∞.
• Underflow dell’esponente :
un esponente negativo è più piccolo del minimo valore permesso (ad esempio –200 è più piccolo di –127). Questo significa che il numero è troppo piccolo per essere rappresentato e può essere considerato 0.
• Owerflow della mantissa :
nel processo di allineamento delle mantisse, le cifre possono scorrere oltre l’estremità destra della mantissa. Come si vedrà, sono richiesti dei meccanismi di arrotondamento.
• Underflow della mantissa :
l’addizione di due mantisse dello stesso segno può causare il riporto di un bit oltre la posizione del bit più significativo. Questo inconveniente, come si vedrà in seguito, può essere risolto con un riallineamento.
Capitolo 3 : Aritmetica in virgola mobile pag. 45
3.2 Addizione e sottrazione
Nell’aritmetica in virgola mobile, l’addizione e la sottrazione sono operazioni più complesse della moltiplicazione e della divisione, a causa della necessità dell’allineamento. L’algoritmo per l’addizione e la sottrazione si divide in quattro fasi basilari:
1. Confronto con lo zero.
2. Allineamento delle mantisse.
3. Addizione o sottrazione delle mantisse. 4. Normalizzazione del risultato.
Un tipico diagramma di flusso è mostrato nella Figura 3.2. Per mettere alla luce le principali funzionalità richieste, segue una spiegazione passo passo dell’algoritmo ipotizzando per i numeri in virgola mobile il formato previsto dallo standard IEEE 754. Per le operazioni di addizione e sottrazione i due operandi devono essere trasferiti nei registri che saranno usati dal sistema di elaborazione e, se il formato per i numeri in virgola mobile include un bit implicito nella mantissa, questo deve essere reso esplicito nel corso dell’operazione. Poiché l’addizione e la sottrazione sono operazioni identiche a meno di un bit segno, il processo inizia cambiando il bit di segno del sottraendo se l’operazione è una sottrazione. Successivamente, se uno degli operandi è zero, l’altro viene restituito come risultato. La fase successiva consiste nel manipolare i numeri in modo da rendere uguali i due esponenti. Per capire la necessità di questo passo, si consideri la seguente addizione decimale :
) 10 456 ( ) 10 123 ( ⋅ 0 + ⋅ −2
Chiaramente, non possiamo subito sommare le due mantisse, in quanto le cifre devono essere poste in posizioni equivalenti, ovvero il 4 del secondo numero deve essere allineato con il 3 del primo. Sotto queste condizioni, i
due esponenti saranno uguali e questa è la condizione matematica per la quale due numeri in questa forma possono essere sommati. Quindi :
0 0 0 2 0) (456 10 ) (123 10 ) (4,56 10 ) 127,56 10 10 123 ( ⋅ + ⋅ − = ⋅ + ⋅ = ⋅
L’allineamento può essere ottenuto facendo scorrere il numero più piccolo verso destra (aumentando il suo esponente) oppure facendo scorrere il numero più grande verso sinistra (diminuendo il suo esponente). Poiché entrambe le operazioni possono causare la perdita di cifre, quello che viene fatto scorrere è il numero più piccolo, in quanto ogni cifra eventualmente persa ha un’importanza relativamente bassa. L’allineamento viene così ottenuto facendo scorrere di un bit verso destra la parte significativa della mantissa incrementando l’esponente, finchè i due esponenti si equivalgono. Se questo processo causa l’azzeramento della mantissa, allora come risultato viene restituito l’altro numero. In questo modo, se due numeri hanno esponenti che differiscono in maniera significativa, il numero più piccolo viene perso. Poi le due mantisse sono aggiunte l’una all’altra tenendo conto dei loro segni e, poiché questi possono differire, il risultato potrebbe essere 0. C’è anche la possibilità che, per una cifra, avvenga un overflow della mantissa: in questo caso, la mantissa del risultato viene fatta scorrere verso destra e l’esponente viene incrementato. Potrebbe avvenire anche un overflow dell’esponente, ma in questo caso viene riferito ciò che è accaduto e l’operazione viene interrotta. Successivamente si ha la normalizzazione del risultato che consiste nel far scorrere le cifre della mantissa verso sinistra, finchè la cifra più significativa non è nulla. Ogni scorrimento causa un decremento dell’esponente e, quindi, potrebbe causare un underflow dell’esponente. Infine, il risultato deve essere arrotondato e quindi restituito. La trattazione sull’arrotondamento verrà fatta dopo la trattazione della moltiplicazione e della divisione.
Addizione SI X = 0 ? Sottrazione Z ← X NO SI Y = 0 ? Cambia il bit di segno Y Scorri la mantissa a destra Z ← 0 Ritorna Aumenta l’esponente più piccolo Somma le mantisse con segno Mantissa =0 ? Mantissa =0 ? Gli esponenti sono uguali ? Overflow di mantissa? Risultati normalizzati? Scorri a destra la mantissa Scorri a sinistra la mantissa NO Overflow di esponente? Incrementa l’esponente Underflow di esponente? Decrementa l’esponente Metti l’altro numero in Z Ritorna Segnala Underflow Ritorna Ritorna Segnala Overflow NO NO NO NO SI SI NO NO SI SI SI SI SI NO Arrotonda il risultato Ritorna Ritorna Z ← Y
3.3 Moltiplicazione e divisione
La moltiplicazione e la divisione in virgola mobile sono processi più semplici dell’addizione e della sottrazione. Consideriamo per prima la moltiplicazione illustrata nel diagramma seguente:
Ritorna SI X = 0 ? NO Overflow dell’esponente? Underflow dell’esponente? Normalizza Arrotonda NO Moltiplica le mantisse Ritorna SI Segnala L’underflow SI Segnala l’overflow Y = 0 ? Somma gli esponenti SI NO NO Z ← 0 Sottrai il fattore di polarizzazione Moltiplicazione
Capitolo 3 : Aritmetica in virgola mobile pag. 49
Se un operando è zero, allora viene restituito come risultato zero. Il passo successivo è sommare i due esponenti: se questi sono memorizzati in forma polarizzata, la loro somma raddoppia il fattore di polarizzazione, che, quindi, deve essere sottratto dalla somma. Se il risultato è un overflow o un underflow di esponente deve essere riferito terminando l’algoritmo. Se l’esponente del prodotto è all’interno dell’intervallo corretto, il passo successivo è moltiplicare le mantisse tenendo conto dei loro segni. La moltiplicazione è eseguita nello stesso modo in cui viene eseguita tra i numeri interi: anche se, in questo caso, si ha a che fare con una rappresentazione modulo e segno, i dettagli sono simili a quelli per la rappresentazione in complemento a due. Il prodotto avrà lunghezza doppia rispetto alle lunghezze del moltiplicano e del moltiplicatore ed i bit extra saranno persi durante l’arrotondamento. Dopo che il prodotto è stato calcolato il risultato è quindi normalizzato ed arrotondato come si è fatto per l’addizione e la sottrazione (si noti che la normalizzazione potrebbe restituire un underflow dell’esponente).
Consideriamo, infine, il diagramma di flusso per la divisione illustrato nella figura 3.3. Ancora una volta, il primo passo è verificare se uno dei due operandi è nullo: se il divisore è zero, il risultato restituito sarà un errore oppure infinito a seconda delle implementazioni, mentre se il dividendo è zero, il risultato restituito sarà zero. Successivamente, l’esponente del divisore viene sottratto dall’esponente del dividendo: questa operazione cancellerà la polarizzazione che, quindi, dovrà essere ristabilita sommando il fattore di polarizzazione. Dopo i test per l’overflow e l’underflow dell’esponente, si passa alla divisione delle mantisse che è seguita dalla normalizzazione e dall’arrotondamento.
Ritorna X = 0 ? Somma il fattore di polarizzazione Y = 0 ? Overflow dell’esponente? Underflow dell’esponente? Sottrai gli esponenti Z ← 0 SI Normalizza Arrotonda Ritorna Dividi le mantisse Segnala L’underflow Segnala l’overflow SI SI ← Z ∞ SI NO NO NO NO Divisione
Capitolo 3 : Aritmetica in virgola mobile pag. 51
3.4 Considerazioni sulla precisione
Bit di guardia.
Si è accennato al fatto che, prima di un’operazione in virgola mobile, l’esponente e la mantissa di ogni operando sono caricati in registri del sistema di elaborazione. Nel caso della mantissa, l’ampiezza del registro è quasi sempre maggiore della lunghezza della mantissa più i, bit implicito. Il registro contiene bit addizionali, chiamati di guardia, che sono usati per riempire di zeri la parte destra della mantissa. Il motivo che spinge verso l’uso dei bit di guardia è illustrato nella figura seguente:
= x 1,000...00⋅21 = − y 0,111...11⋅21 = z 0,000...01⋅21 = 1,000...00⋅222
(a) Esempio binario, senza bit di guardia = x 1,000...00 0000⋅21 1 = − y 0,111...11 1000⋅2 1 2 1000 00 ... 000 , 0 ⋅ = z = 1,000...00 0000⋅2−23 (b) Esempio binario, con bit di guardia
Figura 3.4 – Uso dei bit di guardia
Si considerino numeri nel formato IEEE che hanno la mantissa di 24 bit includendo il bit implicito a sinistra della virgola. Due numeri che hanno
valori molto vicini tra di loro sono e Y . Se il
numero più piccolo deve essere sottratto dal più grande, c’è bisogno di uno scorrimento verso destra di una posizione per allineare gli esponenti (si veda la Figura 3.4). Così facendo, Y perde un bit nella mantissa ed il
risultato è ; questa stessa operazione è ripetuta nella Figura 3.4b con
l’aggiunta del bit di guardia. In questo caso, il bit meno significativo non 1 2 00 ... 00 , 1 ⋅ = X =1,11...11⋅20 22 2−
viene perso a causa dell’allineamento ed il risultato è , c’è una differenza di un fattore 2 dal precedente calcolo.
23 2−
Arrotondamento
Un altro particolare che incide sulla precisione del risultato è l’arrotondamento. Il risultato di un’operazione sulle mantisse è di solito immagazzinato in un registro più grande di questi e, quando il risultato viene riportato in virgola mobile, i bit in eccesso devono essere eliminati. Sono state studiate diverse tecniche per eseguire l’arrotondamento e, per questo, lo standard IEEE elenca quattro approcci alternativi:
• Arrotondamento al più vicino: il risultato è arrotondato al numero rappresentabile più vicino.
• Arrotondamento verso +∞ : il risultato è arrotondato verso l’alto. • Arrotondamento verso −∞ : il risultato è arrotondato verso il basso. • Arrotondamento verso 0 : il risultato viene arrotondato verso lo zero. L’arrotondamento al più vicino è il metodo di arrotondamento di default tra quelli elencati nello standard ed è definito come segue: al numero a precisione illimitata viene assegnato il numero rappresentabile più vicino e, se esistono due valori rappresentabili alla stessa distanza, sarà scelto quello col suo bit meno significativo impostato a zero.
Ad esempio, se i bit in eccesso, oltre i 23 bit che possono essere immagazzinati, sono 10010, allora, poiché il valore di questi ammonta a più della metà del valore dell’ultimo bit rappresentabile, l’arrotondamento corretto è aggiungere 1 all’ultimo bit rappresentabile, avendo così arrotondato al numero rappresentabile successivo. Nel caso in cui i bit extra siano 01111, poiché questi ammontano a meno della metà del valore dell’ultimo bit rappresentabile, l’arrotondamento corretto consiste
Capitolo 3 : Aritmetica in virgola mobile pag. 53
semplicemente nel rinunciare ai bit in eccesso (troncamento) e l’effetto di questa operazione causa l’arrotondamento verso un numero rappresentabile più piccolo. Lo standard da indicazioni anche per il caso speciale in cui i bit extra sono della forma 10000: in questo caso, il risultato è esattamente a metà tra i due possibili valori rappresentabili. Una possibile tecnica, che è anche la più semplice, sarebbe quella di troncare sempre, ma lo svantaggio di questo approccio è che esso introduce una polarizzazione piccola ma cumulativa all’interno di una sequenza di computazioni. Quello che si richiede è un metodo di depolarizzazione dell’arrotondamento ed una possibile soluzione sarebbe arrotondare verso il basso o verso l’alto sulla base di un numero casuale in maniera tale che, in media, il risultato sarebbe non polarizzato: l’argomento contro questa soluzione è che essa non produce risultati deterministici e prevedibili. La soluzione adottata dallo standard IEEE è forzare il risultato ad essere pari: se il risultato di una computazione è esattamente nel mezzo tra due numeri rappresentabili, il valore è arrotondato verso l’alto se l’ultimo bit rappresentabile è un 1 oppure viene troncato se l’ultimo bit rappresentabile è uno 0.
L’arrotondamento verso 0 è in effetti un semplice troncamento (i bit in eccesso sono ignorati). Questa è sicuramente la tecnica più semplice, ma il risultato è che il modulo del valore troncato è sempre minore o uguale al valore originale, introducendo così nell’operazione una consistente polarizzazione verso lo zero: questa polarizzazione è più seria di quella affrontata precedentemente perché essa condiziona in ogni operazione il cui risultato non presenta bit in eccesso nulli.
3.5 Lo standard IEEE per l’aritmetica binaria in virgola mobile
Lo standard IEEE 754 va oltre la semplice definizione di un formato e stabilisce specifiche pratiche e procedure, così che l’aritmetica in virgola mobile possa produrre risultati uniformi, prevedibili ed indipendenti dalla piattaforma hardware. Un aspetto di questo, l’arrotondamento, è gia stato discusso, ora ci occuperemo di: l’infinito, i NaN ( Not a Number) ed i numeri denormalizzati.
Infinito
L’aritmetica dell’infinito è trattata come un caso ristretto dell’aritmetica reale, dando ai valori infiniti la seguente interpretazione:
∞
− < (tutti i numeri finiti) < +∞
Con l’eccezione dei casi speciali discussi in seguito, ogni operazione aritmetica che coinvolge l’infinito produce gli ovvi risultati della tabella 3.2 di seguito riportata.
( )
+∞ =+∞ + 7 7÷( )
+∞ =+0( )
+∞ =−∞ − 7 7÷( )
−∞ =−0( )
−∞ =−∞ + 7( ) ( )
+∞ + +∞ =+∞( )
−∞ =+∞ − 7( ) ( )
−∞ + −∞ =−∞( )
+∞ =+∞ ⋅ 7( ) ( )
−∞ − +∞ =−∞( )
−∞ =−∞ ⋅ 7( )
+∞ −(−∞)=+∞Capitolo 3 : Aritmetica in virgola mobile pag. 55
NaN silenziosi e NaN di segnalazione
Un NaN è un’entità simbolica codificata nel formato in virgola mobile, che può essere di due tipi: NaN di segnalazione e NaN silenzioso. Un NaN di segnalazione indica un’operazione non valida ogni volta che esso compare come un operando: tali NaN offrono valori per le variabili non inizializzate e miglioramenti di tipo aritmetico che non sono oggetto dello standard. Un NaN silenzioso si propaga attraverso quasi tutte le operazioni aritmetiche senza segnalare eccezioni e la tabella 3.3 elenca le operazioni che producono un NaN silenzioso.
Operazione NaN silenzioso prodotto da
Qualsiasi Qualsiasi operazione su
un NaN di segnalazione Addizione e sottrazione Sottrazione di infiniti:
( ) (
+∞ − +∞)
( ) (
−∞ − −∞)
( ) (
−∞ + +∞)
( )
+∞ +(−∞) Moltiplicazione ∞0⋅ Divisone 0 0 oppure ∞ ∞Resto x mod 0 oppure mod y ∞
Radice quadrata x dove x<0
Tabella 3.3 – Operazioni che producono un NaN silenzioso
Si osservi che entrambi i tipi di NaN hanno lo stesso formato generale : un esponente di tutti uno ed una mantissa non nulla. La sequenza di bit reale
della mantissa non nulla dipende dall’implementazione ed i valori della mantissa possono essere usati per distinguere i NaN silenziosi dai NaN di segnalazione e per specificare particolari condizioni di eccezione.
Numeri denormalizzati
I numeri denormalizzati sono inclusi nello standard IEEE 754 per manipolare anche i casi in cui si verifica un underflow di esponente: infatti, quando l’esponente del risultato diventa troppo piccolo (un esponente negativo con un modulo troppo grande), il risultato viene denormalizzato con uno scorrimento verso destra dei bit della mantissa ed incrementando di uno il valore dell’esponente per ogni scorrimento fatto finchè il valore dell’esponente ricade in un intervallo rappresentabile. La Figura 3.4 mostra gli effetti dell’addizione di numeri denormalizzati: i numeri rappresentabili possono essere raggruppati in intervalli della forma
[
2n,2n+1]
, all’interno dei quali la parte del numero che rappresenta l’esponente resta costante mentre la mantissa varia, producendo una spaziatura uniforme di numeri rappresentabili all’interno dell’intervallo. Man mano che ci si avvicina allo zero, ogni intervallo successivo ha una larghezza che è la metà di quella dell’intervallo precedente, ma contiene la stessa quantità di numeri rappresentabili. Quindi la densità dei numeri rappresentabili cresce con l’avvicinarsi allo zero. Tuttavia, se vengono usati soltanto numeri normalizzati, c’è un divario tra il più piccolo numero normalizzato e lozero: nel caso del formato a 32 bit IEEE 754, ci sono numeri
rappresentabili in ogni intervallo ed il numero positivo rappresentabile più
piccolo è . Con l’utilizzo dei numeri denormalizzati sono aggiunti altri
numeri nell’intervallo tra 0 e . Ci si riferisce all’uso dei numeri
denormalizzati come underflow graduale. Senza i numeri denormalizzati, il divario tra il numero rappresentabile più piccolo non nullo e lo zero è molto più ampio del divario tra il numero rappresentabile più piccolo non nullo ed
23 2 126 2− 23 2 2−126
Capitolo 3 : Aritmetica in virgola mobile pag. 57
il numero più grande successivo. L’underflow graduale colma questo divario e riduce l’impatto dell’underflow dell’esponente ad un livello confrontabile con l’arrotondamento tra numeri normalizzati.
Divario
0 2−126 2−125 2−124 2−123 (a) Formato a 32 bit senza numeri denormalizzati
Spaziatura uniforme
0 2−126 2−125 2−124 2−123 (b) Formato a 32 bit con numeri denormalizzati
Figura 3.5 – L’effetto dei numeri denormalizzati nello standard IEEE 754
Bibliografia
[3.1] William Stallings, “Architettura e organizzazione dei calcolatori” Per tutto il capitolo.