CodeGym /Corsi /C# SELF /Problemi di precisione e valori speciali

Problemi di precisione e valori speciali

C# SELF
Livello 6 , Lezione 3
Disponibile

1. Introduzione

Sembra che se il computer è "intelligente", allora 0.1 + 0.2 dovrebbe essere semplicemente 0.3. Ma non è proprio così. Vediamo insieme con un esempio semplice.

double x = 0.1;
double y = 0.2;
double sum = x + y;
Console.WriteLine(sum); // Cosa stampa il programma?
Somma di numeri floating point: ci aspettiamo 0.3, ma cosa succede davvero?

Ora prova a confrontare con 0.3:

Console.WriteLine(sum == 0.3); // Qui sarà true o false?

Se vedi False, non sorprenderti!

Il motivo sta nella rappresentazione dei numeri in memoria

I computer lavorano con i numeri in binario. Ma non tutte le frazioni decimali possono essere rappresentate come frazioni binarie finite, proprio come 1/3 non può essere scritto esattamente come frazione decimale (0.333...). Per esempio, 0.1 in binario è una frazione infinita, e bisogna "arrotondarla" per salvarla.

In parole povere, double a volte "fa finta" di salvare il tuo numero esattamente, ma in realtà salva solo un valore molto vicino.

2. Quali "stranezze" succedono con l'aritmetica dei double?

Vediamo qualche esempio pratico.

Esempio 1. La classica "magia" di 0.1 + 0.2

double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine(sum);            // 0.30000000000000004
Console.WriteLine(sum == 0.3);     // False

Il computer non ha stampato 0.3, ma 0.30000000000000004. La differenza è piccola, ma se lavori, per esempio, con i soldi — diventa un problema serio.

Esempio 2. Somma iterativa

double result = 0;
for (int i = 0; i < 10; i++)
{
    result += 0.1;
}
Console.WriteLine(result); // 0.9999999999999999

Volevi 1.0 — hai ottenuto un po' meno. Di nuovo, il motivo è l'arrotondamento interno di double.

Perché è importante nei casi reali

Molti pensano: "Che importa, è solo un piccolo errore, chi se ne frega!" Vediamo un esempio dal mondo dei pagamenti.

Supponiamo che la tua banca online sommi 100 transazioni da 0.1 euro. Se il tuo programma "perde" un centomillesimo di euro ad ogni iterazione, su scala bancaria hai già "perso" soldi veri. E qui il contabile verrà subito da te e chiederà: "Dove sono i nostri soldi?!"

3. Come confrontare correttamente i numeri floating point

Dato che double spesso non può salvare esattamente il valore che ti aspetti, il confronto diretto con == può "fregarti". Invece, si confronta il valore assoluto della differenza con un numero molto piccolo (epsilon).

Esempio di confronto con tolleranza

double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 0.000001;

if (Math.Abs(a - b) < epsilon)
{
    Console.WriteLine("Quasi uguale!"); // Così è più sicuro confrontare
}

Qui diciamo: "Se la differenza tra i numeri è meno di un milionesimo, li consideriamo uguali".

4. Valori speciali di double: Infinity, NaN, -Infinity

Il tipo double non salva solo numeri, ma anche valori speciali. Succedono in situazioni che un matematico vieterebbe a uno studente normale.

Infinito (Infinity)

Cosa succede se dividi 1 per 0?

double result = 1.0 / 0.0;
Console.WriteLine(result); // Infinity

In C# (e in molti linguaggi) la divisione per 0 per double non lancia eccezioni! Invece il risultato diventa il valore speciale "infinito positivo".

Meno infinito (-Infinity)

Se dividi un numero negativo per 0, ottieni meno infinito:

double result = -1.0 / 0.0;
Console.WriteLine(result); // -Infinity

"Non un numero" (NaN — Not a Number)

Se fai qualcosa di davvero strano, tipo calcolare la radice quadrata di un numero negativo:

double result = Math.Sqrt(-1);
Console.WriteLine(result); // NaN

Oppure il risultato di 0.0 / 0.0:

double result = 0.0 / 0.0;
Console.WriteLine(result); // NaN

NaN — è "tutto ciò che non sarebbe un numero nella vita reale".

Come controllare i valori speciali

In C# ci sono funzioni per controllare i valori speciali:

Console.WriteLine(double.IsInfinity(result));    // true se infinito
Console.WriteLine(double.IsNaN(result));         // true se NaN

Tabella: Come double reagisce alle operazioni strane

Operazione Risultato Cosa salva double
1.0 / 0.0
Infinity +∞
-1.0 / 0.0
-Infinity -∞
0.0 / 0.0
NaN Non un numero
Math.Sqrt(-1)
NaN Non un numero
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION