2011-01-12 20 views
12

Potrebbe qualcuno qui per favore aiutarmi a capire come determinare quando le limitazioni in virgola mobile causeranno errori nei calcoli. Ad esempio il seguente codice.Comprensione dei problemi in virgola mobile

CalculateTotalTax = function (TaxRate, TaxFreePrice) { 
    return ((parseFloat(TaxFreePrice)/100) * parseFloat(TaxRate)).toFixed(4); 
}; 

Non sono stato in grado di immettere due valori che hanno causato per me un risultato errato per questo metodo. Se rimuovo il toFixed (4) posso infatti vedere dove i calcoli iniziano a perdere precisione (da qualche parte intorno al 6 ° decimale). Detto questo, però, la mia comprensione dei float è che anche i numeri piccoli possono a volte non essere rappresentati o ho frainteso e possono essere rappresentati in modo accurato 4 cifre decimali (ad esempio).

MSDN spiega come galleggianti such ...

Questo significa che non possono contenere un esatto rappresentazione di qualsiasi quantitativo non è una frazione binaria (di forma k/ (2^n) dove k e n sono numeri interi)

Ora presumo che questo si applica a tutti i float (inclusi quelli utilizzati in javascript).

Fondamentalmente la mia domanda si riduce a questo. Come si può determinare se qualche metodo specifico sarà vulnerabile agli errori nelle operazioni in virgola mobile, a quale precisione si verificheranno quegli errori e quali input saranno necessari per produrre quegli errori?

Speriamo che quello che sto chiedendo abbia senso.

+0

Forse questo è relativo a questo argomento: http://en.wikipedia.org/wiki/Machine_epsilon –

+0

* Tony the Pony *, noto anche come [Jon Skeet] (http://stackoverflow.com/users/22656/jon-skeet), ha una [spiegazione eccellente] (http://codeblog.jonskeet.uk/2009/11/02/omg-ponies-aka-humanity-epic-fail/). Leggibile, chiaro, divertente senza essere irritante, e non pesante come [alcuni link] (http://docs.sun.com/source/806-3568/ncg_goldberg.html) che sono spesso pubblicati su questo argomento. – MarkJ

risposta

10

iniziare leggendo Ciò che ogni computer Scientist dovrebbe sapere virgola mobile: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Risposta breve: float precisione doppia (che sono l'impostazione predefinita in JavaScript) hanno circa 16 cifre decimali di precisione. L'arrotondamento può variare da una piattaforma all'altra. Se è assolutamente essenziale ottenere la risposta giusta, dovresti fare da solo l'aritmetica razionale (questo non deve essere difficile - per la valuta, forse puoi semplicemente moltiplicare per 100 per memorizzare il numero di centesimi come numero intero).

Ma se è sufficiente per ottenere la risposta con un alto grado di precisione, i galleggianti dovrebbero essere abbastanza buoni, specialmente la doppia precisione.

4

ci sono due cosa importante che dovrebbe ora quando si tratta con i galleggianti:

1- È necessario essere consapevoli di machine epsilon. Per sapere quanta precisione hai.

2- You non deve assumere if two values are equal in base 10, they are equal in base 2 in a machine with precision limit.

if ((6.0/10.0)/3.0 != .2) { 
     cout << "gotcha" << endl; 
} 

numero 2 può essere abbastanza convincente per farvi evitare confrontando i numeri in virgola mobile per l'uguaglianza, invece una soglia e di maggiore o minore di operatori possono essere utilizzati per il confronto

+1

Il numero 2 non implica che "non dovresti mai confrontare i numeri in virgola mobile per l'uguaglianza"; dopotutto, seguirebbe altrettanto facilmente che non dovresti mai confrontare i numeri decimali per l'uguaglianza. Significa che non si deve scrivere codice che non si capisce. Prenditi il ​​tempo per imparare come funziona il floating point. –

+0

Come hai detto, si dovrebbe prendere tempo per imparare a virgola mobile; qualcosa come (0.1 == 1/10) sembra così innocente che nessuno probabilmente penserà che sta causando un bug a meno che non lo abbia letto prima. Anche parlando di nozioni di base sulla matematica, non molte persone sanno che 10 = 9.999 .... o come convertire un decimale in binario. –

+1

Il motivo '.1! = 1/10' è perché' 1/10 == 0'. Stai usando la divisione intera. –

1

No, il numero di i decimali non hanno nulla a che fare con ciò che può essere rappresentato.

Prova .1 * 3, o 162.295/10 o 24.0 + 47.98. Quelli falliscono per me in JS. Ma, 24.0 * 47.98 non fallisce.

Quindi, per rispondere alle tre domande, qualsiasi operazione per qualsiasi precisione è potenzialmente vulnerabile. Se un dato input sarà o meno una domanda, non so come rispondere, ma ho l'impressione che ci siano una serie di fattori. 1) Quanto vicino la risposta effettiva è alla frazione binario più vicina. 2) La precisione nel motore che esegue il calcolo. 3) Il metodo utilizzato per eseguire il calcolo (ad es. Moltiplicare per lo spostamento di bit può dare risultati diversi rispetto alla moltiplicazione per aggiunta ripetuta)

2

Le altre risposte hanno indicato buone risorse per la comprensione di questo problema. Se utilizzi realmente valori monetari nel tuo codice (come nel tuo esempio), dovresti preferire i tipi Decimal (System.Decimal in .Net). Questi eviteranno alcuni problemi di arrotondamento dall'usare i float e far corrispondere meglio il dominio.