Apri l'interprete Python e digita 0.1 + 0.2 — invece di 0.3, ottieni 0.30000000000000004. Non è un bug specifico di Python: è una conseguenza diretta di come lo standard IEEE 754 rappresenta i numeri frazionari in binario, e lo stesso risultato compare in JavaScript, Java, C# e praticamente qualsiasi altro linguaggio basato su questo standard.
Le tre parti di un numero in virgola mobile
Un numero IEEE 754 è memorizzato come tre componenti: un segno (positivo o negativo), una mantissa (le cifre significative del numero) e un esponente (la potenza di cui viene spostata la virgola). È simile alla notazione scientifica: 1.5 × 10², ma in binario.
Perché 0.1 non può essere rappresentato esattamente in binario
0.1 in decimale è una frazione infinita in binario (in modo simile a come 1/3 è infinito in decimale). Un computer memorizza solo un numero limitato di bit di mantissa, quindi il numero viene arrotondato al valore rappresentabile più vicino — ed è proprio questo arrotondamento a causare l'"errore" visibile.
Dove questo crea davvero problemi: il totale del carrello
Una pagina di checkout che somma i prezzi dei singoli articoli come float semplici può discostarsi di un centesimo o due dal totale atteso dopo un numero sufficiente di articoli — ogni addizione arrotonda leggermente, e gli errori si accumulano invece di annullarsi a vicenda. Per questo i sistemi di pagamento lavorano solitamente in centesimi interi, oppure usano un tipo decimal dedicato, invece di sommare direttamente valori float.
A cosa serve
- Capire e diagnosticare errori di arrotondamento inattesi in calcoli finanziari o scientifici.
- Vedere la rappresentazione binaria esatta di un numero in virgola mobile specifico.
- Spiegare a un collega o studente perché confrontare numeri frazionari per uguaglianza esatta sia una cattiva pratica.
I numeri subnormali vicino allo zero
Quando il valore di un numero è troppo piccolo per la rappresentazione normale (una mantissa che inizia con un 1 implicito), IEEE 754 passa alla modalità subnormale (denormalizzata), dove questo 1 implicito viene omesso — questo permette di rappresentare numeri ancora più piccoli in valore assoluto, al costo di una perdita graduale di precisione invece di un salto brusco allo zero. Questa transizione "morbida" verso lo zero è preferibile a un azzeramento improvviso, ma i calcoli subnormali sono notevolmente più lenti di quelli normali su alcuni processori.