Frage

ich Code schreibe, die mit Währungen, Gebühren befassen, usw. ich die BigDecimal Klasse für Mathematik und Speicher verwenden werde, aber wir liefen in etwas seltsam mit ihm.

Diese Aussage:

1876.8 == BigDecimal('1876.8')

false zurück.

Wenn ich diese Werte durch einen Formatierungsstring "%.13f" laufen erhalte ich:

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

Beachten Sie die zusätzlichen 2 vom BigDecimal in der letzten Dezimalstelle.

Ich dachte, BigDecimal sollte die Ungenauigkeiten der Speicherung von reellen Zahlen direkt in dem nativen Floating-Point des Computers begegnen. Wo ist dieser 2 kommen?

War es hilfreich?

Lösung

Sie haben Recht, sollte BigDecimal es richtig sein zu speichern, meine beste Vermutung ist:

  • BigDecimal den Wert zu speichern richtig
  • Wenn auf eine Zeichenfolge Formatierungsfunktion übergeben wird BigDecimal als untere Genauigkeit gegossen werden Gleitkommawert, die ... 02 zu schaffen.
  • Wenn Sie direkt mit einem Schwimmer verglichen, hat der Schwimmer eine zusätzliche Nachkommastelle weit über die 20 Sie sehen (klassischer Schwimmer kann nicht verglichen werden behavoir).

So oder so, ist es unwahrscheinlich, genaue Ergebnisse zu erhalten, einen Schwimmer zu einem BigDecimal verglichen wird.

Andere Tipps

Es wird Ihnen nicht so viel Kontrolle über die Anzahl der Dezimalstellen geben, aber der herkömmliche Format Mechanismus für BigDecimal scheint zu sein:

a.to_s('F')

Wenn Sie mehr Kontrolle benötigen, sollten Sie das Geld-Juwel verwenden, Ihre Domain Problem unter der Annahme ist vor allem über Währung.

gem install money

Do not FPU Dezimalstring Fraktionen für die Gleichstellung

vergleichen

Das Problem ist, dass das Gleichheitsvergleich eines schwebenden oder doppelten Wertes mit einem Dezimal-Konstante, die einen Bruchteil enthält, ist selten erfolgreich.

Sehr wenige Dezimalstring Fraktionen haben exakte Werte in der binären FP Darstellung, so Gleichheit Vergleiche werden in der Regel zum Scheitern verurteilt. *

Um Ihre genaue Frage zu beantworten, wird der 2 kommt eine etwas andere Umwandlung der Dezimalstring Fraktion in das Float Format. Da der Anteil nicht genau dargestellt werden kann, ist es möglich, dass zwei Berechnungen unterschiedliche Mengen an Präzision in den Zwischenberechnungen prüfen werden und schließlich das Ergebnis in eine anders 52-Bit-IEEE 754 mit doppelter Genauigkeit Mantisse Ende gerundet wird. Es spielt kaum eine Rolle, weil es ist keine genaue Darstellung sowieso, aber man ist wahrscheinlich falsch als die anderen.

Insbesondere Ihre 1876.8 können nicht von einem FP-Objekt dargestellt werden genau sein, in der Tat, zwischen 0,01 und 0,99, nur 0,25, 0,50 und 0,75 haben binäre genaue Darstellungen. Alle anderen, umfassen 1876,8, wiederholen Sie für immer und sind auf 52 Bits gerundet. Das ist etwa die Hälfte des Grundes, dass BigDecimal überhaupt existiert. (Die andere Hälfte des Grundes ist die feste Genauigkeit der Daten FP:. Manchmal benötigen Sie mehr)

So, dass Sie das Ergebnis erhalten, wenn eine tatsächliche Maschine-Wert mit einem Dezimal-String-Konstante hängt in der Binärbruch auf jedes einzelne Bit zu vergleichen ... bis auf 1/2 52 ... und selbst dann erfordert Rundung.

Wenn es etwas gibt, auch nur die geringste Bit (hehe, bit, sorry) unvollkommen über den Prozess, der die Anzahl erzeugt, oder den Eingangsumwandlungscode, oder irgendetwas anderes beteiligt ist, werden sie nicht aussehen genau gleich.

Ein Argument selbst gemacht werden könnte, dass der Vergleich sollte immer scheitern, weil kein IEEE-Format FPU auch diese Zahl genau darstellen kann. Sie sind wirklich nicht gleich, auch wenn sie, wie es aussieht. Auf der linken Seite hat Ihr Dezimalstring in eine binären Zeichenfolge umgewandelt worden, und die meisten der Zahlen konvertieren einfach nicht genau. Auf der rechten Seite ist es immer noch eine Dezimalstring.

So mischt nicht schwimmt mit BigDecimal, vergleichen, nur ein BigDecimal mit einem anderen BigDecimal. (Selbst wenn beiden Operanden ist schwimmt, Prüfung auf Gleichheit erfordert große Sorgfalt oder ein Fuzzy-Test auch nicht vertraut jede formatierte Ziffer:. Ausgabeformatierung werden Reste trägt Weg von der rechten Seite der Fraktion, so dass Sie im allgemeinen nicht gestartet werden Nullen zu sehen, werden Sie nur Müll Werte sehen.)


* Das Problem: Maschinen Zahlen x / 2 n , aber dezimal Konstanten x / (2 n * 5 m ). Ihr Wert als Vorzeichen, Exponent und Mantisse wird die unendlich wiederholt 0 10000001001 1101010100110011001100110011001100110011001100110011... Ironischerweise ist FP-Arithmetik perfekt präzise und Gleichheitsvergleiche perfekt funktionieren gut, wenn der Wert keinen Anteil hat.

als David sagte, ist BigDecimal es richtig speichern

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

liefert 187680000000000000

so, ja, die String-Formatierung ruiniert es

Wenn Sie nicht fraktionierten Cent brauchen, sollten Sie die Speicherung und die Währung als Integer Manipulation, dann durch 100 dividiert, wenn es Zeit ist, angezeigt werden soll. Ich finde, dass einfacher als mit den unvermeidlichen Präzision Problemen Umgang zu speichern und in Gleitkomma-Manipulation.

Unter Mac OS X, ich bin mit ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]

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

Allerdings ist Rubin, ich glaube, Sie in Bezug auf die Nachrichten, die an Objekte denken. Was bedeutet diese Rückkehr für Sie:

BigDecimal('1876.8') == 1876.8

Die beiden sind nicht gleichwertig, und wenn Sie versuchen, BigDecimal die Fähigkeit zu bestimmen, präzise dezimal Gleichheit zu verwenden, sollte es der Empfänger der Nachricht über die Gleichheit zu fragen sein.

Aus dem gleichen Grunde glaube ich nicht die BigDecimal Formatierung durch ein Format Nachricht an das Format-String zu senden ist der richtige Ansatz auch nicht.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top