Lo standard IEEE per il calcolo in virgola mobile (IEEE 754) (ufficialmente: IEEE Standard for Binary Floating-Point Arithmetic (ANSI/IEEE Std 754-1985) o anche IEC 60559:1989, Binary floating-point arithmetic for microprocessor systems) è lo standard più diffuso nel campo del calcolo automatico. Questo standard definisce il formato per la rappresentazione dei numeri in virgola mobile (compreso ±0 e i numeri denormalizzati; gli infiniti e i NaN, "not a number"), e un set di operazioni effettuabili su questi. Specifica inoltre quattro metodi di arrotondamento e ne descrive cinque eccezioni.
Esistono in questo standard quattro formati per i numeri in virgola mobile: a precisione singola (32 bit), precisione doppia (64 bit), precisione singola estesa (≥ 43 bit), raramente usato, e precisione doppia estesa (≥ 79 bit), supportata solitamente con 80 bit. La precisione singola è il minimo richiesto dallo standard, gli altri sono opzionali.
Un numero in virgola mobile, secondo lo standard IEEE è rappresentato su parole di 32, 64 o 128 bit divisi in tre parti:
in questo ordine. Gli n bit di una stringa sono indicizzati in modo decrescente con numeri interi da n-1 a 0. In un numero in questo standard, l'importanza del bit decresce col suo indice.
Di seguito è rappresentato un numero in una stringa di 32 bit:
1 8 23 lunghezza in bit +-+--------+-----------------------+ |S| Esp. | Mantissa | +-+--------+-----------------------+ 31 30 22 0 indice dei bit
Il valore del numero rappresentato è calcolabile come:
Il campo s specifica il segno del numero: 0 per i numeri positivi, 1 per i numeri negativi. Il campo e contiene l'esponente del numero in forma intera. Essendo costituito da 8 bit, permette di rappresentare 256 valori. I valori 0 e 255 vengono riservati per funzioni speciali (descritte in seguito); gli altri permettono di rappresentare 254 valori per i numeri in forma normale, compresi tra -126 e 127, dato che questo campo deve poter rappresentare sia numeri enormi che minimi; tuttavia, adoperando il metodo usato per la rappresentazione del segno dei numeri interi, si creerebbero problemi per il confronto tra numeri. Per risolvere questo problema, il campo è rappresentato in eccesso k detto bias (che è pari a 2^(n-1)-1 con n il numero di bit dell'esponente), per cui:
e = E + k
e reciprocamente
E = e - k
In questo standard, per i numeri a precisione singola, il bias è uguale a 127. In questa rappresentazione (chiamata polarizzazione), i valori dell'esponente compresi tra -126 e 127 assumono invece, nella scrittura del byte, i valori compresi tra 1 e 254, eliminando la necessità di un bit riservato al segno. In fase di decodifica del numero, il bias viene nuovamente sottratto per recuperare il valore originale.
[-126,...,0,...,127] --> [-126+127,...,0+127,...,127+127] --> [1,...,127,...,254] --> [00000001,...,01111111,...,11111110]
I valori assunti dall'esponente e e dalla mantissa m determinano l'appartenenza del numero ad una di queste categorie:
L'esponente distingue i numeri in modo primario, la mantissa in modo secondario.
Categoria | Esp. | Mantissa |
---|---|---|
Zeri | 0 | 0 |
Numeri denormalizzati | 0 | non zero |
Numeri normalizzati | 1-254 | qualunque |
Infiniti | 255 | 0 |
Nan (not a number) | 255 | non zero |
Il campo m è una stringa di bit che rappresenta la sequenza di cifre dopo la virgola. Tutte le mantisse sono normalizzate in modo che il numero prima della virgola sia 1, per cui per un dato m il valore matematico corrispondente è
In pratica, la mantissa è costituita dal numero binario 1, preceduto dalla virgola e dalla parte intera del numero rappresentato, in forma binaria; la mantissa risulta così artificialmente compresa tra 1 e 2. Quando un numero è normalizzato, come risulta dal suo esponente, il primo bit della mantissa, pari a 1, viene omesso per convenienza: viene quindi chiamato bit nascosto, o bit implicito.
Con questo sistema di rappresentazione, si hanno due zeri (+0 e −0) e due infiniti (+∞ e −∞) a seconda del valore del primo bit. Inoltre i numeri subnormali possono avere un segno e una mantissa, utili però solo per l'analisi.
Questo sistema di rappresentazione permette di avere una precisione relativa x quasi costante per tutti i valori rappresentabili. Infatti
Facciamo un semplice esempio: codifichiamo il numero −118.5 nel sistema IEEE 754.
Dobbiamo determinarne il segno, l'esponente e la mantissa.
Poiché è un numero negativo, il primo bit è "1".
Poi scriviamo il numero in forma binaria: 1110110,1.
Successivamente spostiamo la virgola verso sinistra, lasciando solo un 1 alla sua sinistra: .
La mantissa è la parte a destra della virgola, riempita con zeri a destra fino a riempire i 23 bit: 11011010000000000000000.
L'esponente è pari a 6, ma dobbiamo convertirlo in forma binaria e adattarlo allo standard. Per la precisione singola, dobbiamo aggiungere 127. Quindi 6 + 127 = 133. In forma binaria: 10000101.
Assemblando il tutto:
1 8 23 +-+--------+-----------------------+ |S| Esp | Mantissa | |1|10000101|11011010000000000000000| +-+--------+-----------------------+ 31 30 22 0
La precisione a 64 bit è doppia rispetto a quella da 32 bit assunta come base dallo standard:
1 11 52 +-+-----------+----------------------------------------------------+ |S| Esp | Mantissa | +-+-----------+----------------------------------------------------+ 63 62 51 0
I NaN e gli infiniti sono rappresentati con esponenti formati da una serie di 1 (pari a 2047).
Per i numeri normalizzati il bias è pari a 1023 (quindi e = E + 1023). Per i numeri denormalizzati l'esponente è −1022 (il minimo esponente per un numero normalizzato). Come prima, sia gli infiniti che gli zeri possono essere rappresentati con entrambi i segni.
La precisione decimale è di circa 16 cifre decimali.
La norma regolamenta infine i numeri da 128 bit, che permettono una precisione quadrupla rispetto ai numeri da 32 bit assunti come base dallo standard:
1 15 112 +-+---------------+---------------------------------------------------------------------------------+ |S| Esp | Mantissa | +-+---------------+---------------------------------------------------------------------------------+
Il numero di bits dedicati all'esponente sale da 11 a 15 e quelli per la mantissa a 112.
La precisione totale sale a circa 34 cifre decimali.
Il bias dell'esponente è pari a 16383 (quindi e = E − 16383).
Lo standard è attualmente sotto revisione (IEEE 754r).
Esistono innumerevoli modi per rappresentare numeri in virgola mobile ma il sistema più utilizzato è lo standard IEEE P754; questo metodo comporta l'utilizzo della notazione scientifica, in cui ogni numero è identificato dal segno, da una mantissa (1,xxxxx) e dall'esponente (). La procedura standard per la conversione da numero decimale a numero binario P754 è la seguente:
A questo punto abbiamo raccolto tutti i dati necessari per memorizzare il numero: in base al numero di bit che abbiamo a disposizione possiamo utilizzare tre formati: il formato a precisione singola (32 bit), il formato a precisione doppia (64 bit) e il formato a precisione quadrupla (128 bit).
Per esempio, convertiamo il valore in binario P754 single:
Il numero, alla fine, sarà espresso nel formato:
Prendiamo per esempio il numero negativo frazionario:
-5,828125
Trasformiamo in binario la parte intera:
5:2=2 R=1 2:2=1 R=0 1:2=0 R=1
5(10) = 101(2)
Trasformiamo ora la parte decimale in binario:
0,828125*2=1,65625 U=1 0,65625 *2=1,3125 U=1 0,3125 *2=0,625 U=0 0,625 *2=1,25 U=1 0,25 *2=0,5 U=0 0,5 *2=1 U=1
0,828125(10) = 110101(2)
Uniamo ora le due parti:
101,110101
Spostiamo la virgola due posizioni verso sinistra riscrivendo in questo modo il risultato:
1,01110101*2^2
Otteniamo quindi la parte iniziale della nostra mantissa 01110101, ed avendo spostato verso sinistra la virgola di due posizioni per ottenere lo stesso numero dobbiamo moltiplicare 2 al quadrato.
A questo punto ricaviamo l'esponente sommando 2 al bias:
2+127=129
Trasformiamo questo numero in binario:
129:2=64 R=1 64:2=32 R=0 32:2=16 R=0 16:2= 8 R=0 8:2= 4 R=0 4:2= 2 R=0 2:2= 1 R=0 1:2= 0 R=1
129(10)=10000001(2)
Abbiamo così ottenuto il nostro numero in floating point ricordandoci di mettere ad 1 il bit del segno in quanto siamo partiti da un numero negativo:
-5,828125(10)=1|1000 0001|0111 0101 0000 0000 0000 000(2)
Se vogliamo ora esprimere in esadecimale il numero trovato (senza tener conto dell'eventuale formato Little Endian che modificherebbe la disposizione dei byte) non ci resta che suddividerlo a gruppi di quattro e trovare i valori esadecimali corrispondenti:
1100 0000 1011 1010 1000 0000 0000 0000 C 0 B A 8 0 0 0
In base sedici il nostro numero in floating point standard IEEE 754 sarà:
C0BA8000