Visualizzatore dei bit dei numeri in virgola mobile IEEE 754

Scompone e mostra la configurazione di bit di un numero decimale in precisione singola, a 32 bit, e doppia, a 64 bit, secondo la norma IEEE 754, oppure converte in senso inverso dalla configurazione al decimale. Distingue con colori diversi segno, esponente e mantissa e riconosce NaN, infiniti e numeri denormalizzati.

L'esempio classico: perché 0,1 più 0,2 non fa 0,3

Calcolando `0.1 + 0.2` in JavaScript si ottiene `0.30000000000000004`, che non coincide con `0.3`. Osservando la configurazione di bit in precisione doppia se ne comprende la ragione.

Valore Esadecimale in precisione doppia (64 bit) Binario in precisione doppia (64 bit)
0.1 0x3FB999999999999A 0011111110111001100110011001100110011001100110011001100110011010
0.2 0x3FC999999999999A 0011111111001001100110011001100110011001100110011001100110011010
0.3 0x3FD3333333333333 0011111111010011001100110011001100110011001100110011001100110011
0.1 + 0.2 0x3FD3333333333334 0011111111010011001100110011001100110011001100110011001100110100

In binario 0,1, 0,2 e 0,3 sono tutti numeri periodici infiniti: non entrano nei 52 bit finiti della mantissa e vengono arrotondati. Poiché gli errori di arrotondamento di 0,1 e 0,2 non si compensano, la loro somma risulta in una configurazione che differisce di un solo bit da quella che rappresenta esattamente 0,3, ossia 0x3FD3333333333334. Provate a inserire questi valori nello strumento qui sopra e a confrontare l'ultimo bit.

Consigli per l'uso

  • Convertendo in bit 0,1, 0,2 e 0,3 in precisione doppia e confrontandoli con il risultato di 0,1 più 0,2, cioè 0.30000000000000004, si vede a colpo d'occhio che differiscono per un solo bit finale.
  • Inserendo «-0» nel campo decimale si ottiene una configurazione in cui è acceso soltanto il bit di segno, diversa da quella del più zero. Numericamente 0 e -0 sono uguali, ma la rappresentazione in bit è un'altra cosa.
  • Quando l'esponente è tutto a zero, una mantissa nulla indica lo zero e una mantissa non nulla un numero denormalizzato, cioè la rappresentazione particolare che, a scapito della precisione, esprime valori estremamente vicini allo zero.
  • Passando dalla precisione singola, a 32 bit, a quella doppia, a 64 bit, si osserva come per uno stesso valore 0,1 cambi l'entità dell'errore di arrotondamento, maggiore in precisione singola.
  • Nella conversione inversa a partire dai bit occorre indicare un numero esadecimale con il prefisso `0x` oppure un binario con tante cifre quante ne richiede la precisione scelta, con il prefisso `0b` facoltativo.

Domande frequenti

In binario 0,1, 0,2 e 0,3 sono tutti numeri periodici infiniti e l'elaboratore, per farli entrare in una mantissa finita — 52 bit in precisione doppia — li arrotonda. Poiché gli errori di arrotondamento di 0,1 e 0,2 non si compensano, la loro somma differisce lievemente dalla configurazione che rappresenta esattamente 0,3 e vale 0,30000000000000004. Non è il difetto di un linguaggio particolare, ma un fenomeno comune a tutti quelli conformi alla norma IEEE 754.

La precisione singola, float su 32 bit, si compone di un bit di segno, otto di esponente e ventitré di mantissa; quella doppia, double su 64 bit, di un bit di segno, undici di esponente e cinquantadue di mantissa. Più bit ha la mantissa, maggiore è il numero di cifre significative, cioè la precisione; più ne ha l'esponente, più ampio è l'intervallo dei valori rappresentabili. Nei linguaggi di programmazione il tipo in virgola mobile predefinito è spesso quello a precisione doppia.

Sono le configurazioni con esponente tutto a zero e mantissa diversa da zero. Ai numeri normalizzati si antepone alla mantissa un uno implicito; i denormalizzati vi rinunciano e fissano l'esponente al minimo, riuscendo così a rappresentare valori ancora più vicini allo zero del più piccolo normalmente esprimibile, ma sacrificando le cifre significative.

Nella norma IEEE 754 il bit di segno esiste indipendentemente: anche con esponente e mantissa tutti a zero si hanno perciò due configurazioni distinte, il «più zero» con il bit di segno a zero e il «meno zero» con il bit a uno. Nel confronto numerico 0 e -0 risultano uguali, ma nei calcoli emergono differenze: uno diviso zero dà più infinito, uno diviso meno zero dà meno infinito.

Entrambi hanno l'esponente tutto a uno. Se la mantissa è tutta a zero si tratta dell'infinito, con il bit di segno a distinguere più infinito e meno infinito; se la mantissa è diversa da zero si tratta di NaN, cioè di un valore che non è un numero. Si generano come esito di operazioni matematicamente indefinite, come la divisione per zero o infinito meno infinito.
Tool-kun

A proposito — come nacque la «lingua comune» chiamata IEEE 754

Prima del 1985 ciascun costruttore adottava una propria rappresentazione dei numeri in virgola mobile. Fra IBM, DEC e Cray, calcolando lo stesso valore 0,1 si ottenevano arrotondamenti e intervalli rappresentabili leggermente diversi, e portare un programma da una macchina all'altra faceva puntualmente divergere i risultati numerici. Per porre fine alla confusione, un gruppo guidato da William Kahan dell'Università della California a Berkeley, che aveva collaborato anche alla progettazione del coprocessore matematico Intel 8087, definì nel 1985 la norma IEEE 754 come standard trasversale al settore.

L'idea centrale della norma sta nel ripartire i compiti fra tre parti — segno, esponente e mantissa — per approssimare con un numero finito di bit, 32 o 64, l'infinità dei numeri reali. L'esponente si occupa dell'ordine di grandezza, la mantissa della precisione delle cifre significative: così, con pochi bit, si trattano con duttilità numeri piccolissimi e grandissimi. Il fatto che l'esponente sia memorizzato sommandovi una polarizzazione — 127 in precisione singola e 1023 in quella doppia — è un accorgimento che permette di stabilire quale numero sia il maggiore confrontando semplicemente le sequenze di bit, senza le complicazioni del complemento a due degli interi con segno.

In binario si rappresentano «esattamente» soltanto le frazioni il cui denominatore è una potenza di due: un mezzo, un quarto, tre ottavi. Il valore decimale 0,1 corrisponde alla frazione un decimo, ma dieci non è una potenza di due e in binario diventa il numero periodico infinito 0,0001100110011… Per farlo entrare in una mantissa finita non resta che troncarlo e arrotondarlo: è questa la ragione di fondo per cui 0,1 non è rappresentabile con esattezza. Ed è per lo stesso motivo che i sistemi contabili, che devono trattare esattamente il decimale, ricorrono all'aritmetica intera o a tipi decimali appositi anziché alla virgola mobile.

La norma è stata poi rivista nel 2008 e nel 2019, con l'aggiunta della mezza precisione a 16 bit, della quadrupla a 128 bit e delle disposizioni sui numeri decimali in virgola mobile, ma la struttura fondamentale in tre parti e l'impostazione delle modalità di arrotondamento sono rimaste quelle della prima edizione del 1985. Poiché i tipi float e double di quasi tutti i linguaggi di programmazione vi si conformano, la domanda «perché 0,1 più 0,2 non fa 0,3» descrive un fenomeno comune a ogni linguaggio.