Domanda

Sto scrivendo il codice che si occuperà con le valute, gli oneri, ecc ho intenzione di utilizzare la classe BigDecimal per la matematica e di stoccaggio, ma ci siamo imbattuti in qualcosa di strano con esso.

Questa dichiarazione:

1876.8 == BigDecimal('1876.8')

restituisce false.

Se corro quei valori attraverso una stringa di formattazione "%.13f" ottengo:

"%.20f" % 1876.8 => 1876.8000000000000
"%.20f" % BigDecimal('1876.8') => 1876.8000000000002

Si noti la 2 extra dal BigDecimal all'ultimo decimale.

Ho pensato BigDecimal doveva contrastare le imprecisioni di memorizzazione dei numeri reali direttamente in virgola mobile nativo del computer. Dove si trova questo 2 venuta da?

È stato utile?

Soluzione

Hai ragione, BigDecimal dovrebbe essere la memorizzazione in modo corretto, la mia ipotesi migliore è:

  • BigDecimal memorizza correttamente il valore
  • Quando passato a una funzione stringa di formattazione, BigDecimal viene lanciato come una precisione inferiore valore in virgola mobile, creando il ... 02.
  • Quando confrontato direttamente con un galleggiante, il galleggiante ha un decimale in più ben oltre il 20 che si vede (galleggianti classiche non possono essere confrontati behavoir).

In entrambi i casi, e 'difficile ottenere risultati accurati confronto un galleggiante per un BigDecimal.

Altri suggerimenti

Non si darà più controllo sul numero di cifre decimali, ma il meccanismo formato convenzionale per BigDecimal sembra essere:

a.to_s('F')

Se avete bisogno di un maggiore controllo, è possibile utilizzare la gemma di denaro, assumendo il problema di dominio è in gran parte su di valuta.

gem install money

Non confrontare frazioni stringa FPU decimale per l'uguaglianza

Il problema è che il confronto di uguaglianza di un valore galleggianti o doppia con una costante decimale contenente una frazione è raramente successo.

Molto poche frazioni stringa decimale hanno valori esatti nella rappresentazione binaria FP, in modo da confronti di uguaglianza di solito sono condannati. *

Per rispondere alla tua domanda esatta, il 2 proviene da un po 'diverso conversione della frazione stringa decimale nel formato Float. Poiché la frazione non può essere rappresentato esattamente, è possibile che due calcoli considereranno diverse quantità di precisione nei calcoli intermedi e finiscono per arrotondando il risultato a un IEEE 754 doppia precisione mantissa 52 bit diverso. Poco importa, perché ci è senza rappresentazione esatta in ogni caso, ma è probabilmente più sbagliato rispetto agli altri.

In particolare, proprio 1876.8 non può essere rappresentato da un oggetto esattamente FP, infatti, tra 0,01 e 0,99, solo 0,25, 0,50, 0,75 e hanno rappresentazioni binarie esatti. Tutti gli altri, comprendono 1876,8, ripetere sempre e vengono arrotondati a 52 bit. Questo è circa la metà della ragione che BigDecimal esiste anche. (L'altra metà della ragione è la precisione dei dati fissa FP:. A volte avete bisogno di più)

Quindi, il risultato che si ottiene quando si confrontano un valore macchina reale con una stringa decimale costante dipende da ogni singolo bit nella frazione binario ... fino a 1/2 52 ... e anche allora richiede arrotondamento.

Se c'è qualcosa anche minimamente (hehe, po ', sorry) imperfetta sul processo che ha prodotto il numero o il codice di conversione di ingresso, o qualsiasi altra cosa in questione, che non osserveranno esattamente uguale.

Un argomento potrebbe anche essere fatto che il confronto dovrebbe sempre esito negativo perché nessuno FPU IEEE-formato può anche rappresentare quel numero esattamente. In realtà non sono uguali, anche se sembrano come esso. A sinistra, la stringa decimale è stato convertito in una stringa binaria, e la maggior parte dei numeri semplicemente non convertire esattamente. Sulla destra, è ancora una stringa decimale.

Quindi, non mescolare carri con BigDecimal, basta confrontare un BigDecimal con un altro BigDecimal. (Anche quando entrambi gli operandi sono galleggianti, test per l'uguaglianza richiede grande cura o un test sfocata Inoltre, non si fidano ogni cifra formattato:. Formattazione dell'output porterà resti lontano il lato destro della frazione, in modo da non in genere inizia a vedere zeri, sarà solo vedere i valori della spazzatura.)


* Il problema: i numeri della macchina sono x / 2 n , ma costanti decimali sono x / (2 n * 5 m ). Il vostro valore come segno, esponente e mantissa è l'0 10000001001 1101010100110011001100110011001100110011001100110011... ripetizione all'infinito Ironia della sorte, FP aritmetica è perfettamente preciso e confronti di uguaglianza funziona perfettamente quando il valore non ha alcuna frazione.

come ha detto David, BigDecimal memorizza bene

 p (BigDecimal('1876.8') * 100000000000000).to_i

restituisce 187.680.000 miliardi

Quindi, sì, la formattazione di stringhe sta rovinando lo

Se non avete bisogno di centesimi frazionali, prendere in considerazione lo stoccaggio e la manipolazione della valuta come un intero, quindi dividendo per 100 quando è il momento di visualizzare. Trovo che più facile che fare con i problemi di precisione inevitabili di immagazzinare e manipolare in virgola mobile.

In Mac OS X, sto correndo ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]

irb(main):004:0> 1876.8 == BigDecimal('1876.8') => true

Tuttavia, essendo Ruby, penso che si dovrebbe pensare in termini di messaggi inviati agli oggetti. Che cosa significa questo ritorno a voi:

BigDecimal('1876.8') == 1876.8

I due non sono equivalenti, e se si sta cercando di usare l'abilità di BigDecimal di determinare precisi uguaglianza decimale, dovrebbe essere il destinatario del messaggio chiedendo l'uguaglianza.

Per lo stesso motivo non credo che la formattazione della BigDecimal inviando un messaggio in formato per la stringa di formato è l'approccio giusto sia.

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