2010-01-04 3 views
7

Mi sono imbattuto in questi valori nel mio codice ColdFusion ma la calcolatrice di Google sembra avere lo stesso "bug" in cui la differenza è diversa da zero.Perché questa sottrazione non è uguale a zero?

416582,2850-411476,8100 - 5105,475 = -2.36468622461E-011

http://www.google.com/search?hl=en&rlz=1C1GGLS_enUS340US340&q=416582.2850+-+411476.8100+-+5105.475&aq=f&oq=&aqi=

JavaCast'ing questi lungo/float/double non Aiuto- si traduce in altre differenze non nulli.

+16

Ci dovrebbe essere un quiz in virgola mobile quando si crea un account su SO. Penso che questa sia la domanda più frequente sul sito. – recursive

+3

@recursive: http://meta.stackexchange.com/questions/26621/whats-the-most-repeated-question-on-stackoverflow/26633#26633 –

+1

Quando ho imparato a programmare, la mancanza di precisione nei numeri in virgola mobile è stato menzionato la prima volta in cui è stato discusso il punto in virgola mobile, e diverse volte in seguito. Non lo fanno più? –

risposta

16

Questo perché i numeri decimali che "guardano" intorno alla base 10 non sono esattamente rappresentabili nella base 2 (che è ciò che i computer usano per rappresentare i numeri in virgola mobile). Si prega di consultare l'articolo What Every Computer Scientist Should Know About Floating-Point Arithmetic per una spiegazione dettagliata di questo problema e soluzioni alternative.

+1

5105.475 era il numero che non era rappresentabile esattamente in un doppio. PrecisionEvalutazione è la funzione ColdFusion di cui avevo bisogno. –

+0

In realtà nessuno di questi numeri è esattamente rappresentabile come valori a doppia precisione. –

+0

+1 riferimento eccellente, controlla anche "Come stampare accuratamente i numeri in virgola mobile" di GL Steele –

7

inaccuratezze in virgola mobile (esiste un numero infinito di numeri reali e solo un numero finito di numeri a 32 o 64 bit con cui rappresentarli).

Se non riesci a gestire piccoli errori, devi utilizzare invece BigDecimal.

+1

Riferimento: http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems –

+0

Ma 416582.2850 - 411476.8100 può essere rappresentato * esattamente * come 5105.475. Questi sono numeri con solo 4 decimali, non ripetuti. I 4 decimali sono troppo precisi? –

+2

Non è la rappresentazione di base 10 che deve essere finita, è la rappresentazione di base-2. Vedi http://en.wikipedia.org/wiki/Binary_numeral_system#Fractions_in_binary per una spiegazione di quali frazioni possono essere rappresentate esattamente in binario (sono solo quelle che hanno 2 come fattore primo del denominatore). –

1

Il problema è la rappresentazione inesatta dei tipi a virgola mobile. Poiché questi non possono essere rappresentati esattamente come float, si ottiene una perdita di precisione che provoca errori di piccole dimensioni nelle operazioni. In genere con i float si desidera confrontare se il risultato è uguale a un altro valore all'interno di qualche piccolo episodio (fattore di errore).

2

Poiché il computer memorizza i numeri in binario, i numeri a virgola mobile sono imprecisi. 1E-11 è una piccola differenza dovuta all'arrotondamento di questi numeri decimali al numero binario rappresentabile più vicino.

2

Questo "bug" non è un bug. È come funziona l'aritmetica in virgola mobile. Vedi: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Se volete precisione arbitraria in Java, utilizzare BigDecimal:

BigDecimal a = new BigDecimal("416582.2850"); 
    BigDecimal b = new BigDecimal("411476.8100"); 
    BigDecimal c = new BigDecimal("5105.475"); 
    System.out.println(a.subtract(b).subtract(c)); // 0.0 
1

Questi sono floating point problemi e l'utilizzo di BigDecimal lo risolverà.

Anche la modifica dell'ordine di sottrazione produce zero in Google.

416582.2850 - 5105.475 - 411476.8100 = 0 
5

Usa PrecisionEvaluate() in ColdFusion (si userà BigDecimal in Java)

zero = PrecisionEvaluate(416582.2850 - 411476.8100 - 5105.475); 

differenza Evaulate(), no "" è necessario.

+1

+1 per la soluzione di fusione fredda effettiva – Kip