Perché vedo una doppia variabile inizializzata su un valore come 21.4 come 21.399999618530273?

StackOverflow https://stackoverflow.com/questions/177506

  •  05-07-2019
  •  | 
  •  

Domanda

double r = 11.631;
double theta = 21.4;

Nel debugger, questi sono mostrati come 11.631000000000000 e 21.399999618530273 .

Come posso evitarlo?

È stato utile?

Soluzione

Questi problemi di precisione sono dovuti ai rappresentazione interna di numeri in virgola mobile e non c'è molto che puoi fare per evitarlo.

A proposito, la stampa di questi valori in fase di esecuzione spesso porta ancora ai risultati corretti, almeno usando i moderni compilatori C ++. Per la maggior parte delle operazioni, questo non è un grosso problema.

Altri suggerimenti

Mi è piaciuto la spiegazione di Joel , che si occupa di un simile binario mobile problema di precisione dei punti in Excel 2007:

  

Vedi come c'è un sacco di 0110 0110 0110 alla fine? Questo perché 0.1 non ha nessuna rappresentazione esatta in binario ... è un numero binario ripetuto. È un po 'come il 1/3 non ha rappresentazione in decimale. 1/3 è 0.33333333 e devi continuare a scrivere 3 per sempre. Se perdi la pazienza, ottieni qualcosa di inesatto.

     

Quindi puoi immaginare come, se decimali, se provassi a fare 3 * 1/3 e non avessi il tempo di scrivere 3 per sempre, il risultato che otterrai sarebbe 0.99999999, non 1, e le persone arrabbiarsi con te per aver sbagliato.

Se hai un valore come:

double theta = 21.4;

E tu vuoi fare:

if (theta == 21.4)
{
}

Devi essere un po 'intelligente, dovrai verificare se il valore di theta è veramente vicino a 21.4, ma non necessariamente quel valore.

if (fabs(theta - 21.4) <= 1e-6)
{
}

Questo è in parte specifico della piattaforma e non sappiamo quale piattaforma stai usando.

In parte è anche il caso di sapere cosa in realtà vuoi vedere. Il debugger ti mostra - in una certa misura, comunque - il valore preciso memorizzato nella tua variabile. Nel mio articolo sui numeri binari in virgola mobile in .NET , c'è un classe C # che ti consente di vedere il numero assolutamente esatto memorizzato in un doppio. La versione online al momento non funziona: proverò a crearne una su un altro sito.

Dato che il debugger vede " effettivo " valore, deve fare un giudizio su cosa visualizzare: potrebbe mostrarti il ??valore arrotondato a qualche decimale o un valore più preciso. Alcuni debugger svolgono un lavoro migliore di altri nel leggere le menti degli sviluppatori, ma è un problema fondamentale con i numeri binari in virgola mobile.

Utilizzare il tipo decimale a virgola fissa se si desidera stabilità ai limiti della precisione. Ci sono spese generali e devi espressamente lanciare se desideri convertire in virgola mobile. Se esegui la conversione in virgola mobile, ripristinerai le instabilità che sembrano disturbarti.

In alternativa puoi superarlo e imparare a lavorare con la precisione limitata dell'aritmetica in virgola mobile. Ad esempio puoi usare l'arrotondamento per far convergere i valori, oppure puoi usare i confronti epsilon per descrivere una tolleranza. & Quot; Epsilon " è una costante impostata che definisce una tolleranza. Ad esempio, puoi scegliere di considerare due valori uguali se si trovano entro 0,0001 l'uno dall'altro.

Mi viene in mente che potresti usare il sovraccarico dell'operatore per rendere trasparenti i confronti di epsilon. Sarebbe molto bello.


Per le rappresentazioni di esponente mantissa, EPSILON deve essere calcolato per rimanere nella precisione rappresentabile. Per un numero N, Epsilon = N / 10E + 14

System.Double.Epsilon è il valore positivo rappresentabile più piccolo per il tipo Double . È troppo piccolo per il nostro scopo. Leggi i consigli di Microsoft sui test di uguaglianza

L'ho già visto prima ( su il mio blog ) - Penso che la sorpresa sia che i numeri "irrazionali" sono diversi.

Per 'irrazionale' qui sto solo facendo riferimento al fatto che non possono essere rappresentati accuratamente in questo formato. Numeri irrazionali reali (come & # 960; - pi) non possono essere rappresentati con precisione.

La maggior parte delle persone ha familiarità con 1/3 che non funziona in decimali: 0.3333333333333 ...

La cosa strana è che 1.1 non funziona nei float. Le persone si aspettano che i valori decimali funzionino in numeri in virgola mobile a causa di come li pensano:

  

1.1 è 11 x 10 ^ -1

Quando in realtà sono in base-2

  

1.1 è 154811237190861 x 2 ^ -47

Non puoi evitarlo, devi solo abituarti al fatto che alcuni float sono "irrazionali", come lo è 1/3.

Un modo per evitarlo è utilizzare una libreria che utilizza un metodo alternativo di rappresentazione dei numeri decimali, come BCD

Mi sembra che 21.399999618530273 sia la rappresentazione single precision (float) di 21.4. Sembra che il debugger stia eseguendo il downdown dal doppio al float da qualche parte.

Se stai usando Java e hai bisogno di precisione, usa la classe BigDecimal per i calcoli in virgola mobile. È più lento ma più sicuro.

Non puoi evitarlo poiché stai usando numeri in virgola mobile con una quantità fissa di byte. Semplicemente non c'è alcun isomorfismo possibile tra numeri reali e la sua notazione limitata.

Ma la maggior parte delle volte puoi semplicemente ignorarlo. 21.4 == 21.4 sarebbe ancora vero perché è ancora gli stessi numeri con lo stesso errore. Ma 21.4f == 21.4 potrebbe non essere vero perché l'errore per float e double sono diversi.

Se hai bisogno di precisione fissa, forse dovresti provare i numeri a virgola fissa. O anche numeri interi. Ad esempio, utilizzo spesso int (1000 * x) per passare al cercapersone di debug.

Se ti dà fastidio, puoi personalizzare la modalità di visualizzazione di alcuni valori durante il debug. Usalo con cura :-)

Miglioramento del debug con gli attributi di visualizzazione del debugger

Consulta Aritmetica decimale generale

Prendi nota anche quando confronti i float, vedi questa risposta per ulteriori informazioni.

Secondo il javadoc

" Se almeno uno degli operandi di un operatore numerico è di tipo doppio, allora
    l'operazione viene eseguita utilizzando l'aritmetica a virgola mobile a 64 bit e il risultato della
    operatore numerico è un valore di tipo doppio. Se l'altro operando non è un doppio, è
    prima ampliato (& # 167; 5.1.5) per digitare double per promozione numerica (& # 167; 5.6). "

Ecco la fonte

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top