2009-09-20 17 views
6

Warum gibt dieser Code 7.30 - 7.20 in Rubin 0.0999999999999996 zurück, nicht 0.10?Arithmetik in Rubin

Aber wenn ich 7.30 - 7.16 schreibe, wird zum Beispiel alles in Ordnung sein, ich werde 0.14 bekommen.

Was das Problem, und wie kann ich es lösen?

Antwort

1

Dies ist ein häufiger Fehler bei der Darstellung von Gleitkommazahlen im Speicher.

Verwenden Sie BigDecimal, wenn Sie genaue Ergebnisse benötigen.

result=BigDecimal.new("7.3")-BigDecimal("7.2") 
puts "%2.2f" % result 
+0

Es gibt eine ziemlich gute Diskussion dazu auf: http://whynotwiki.com/Ruby_/_Numbers –

+2

Beachten Sie, dass BigDecimal nur dann exakte Ergebnisse liefert, wenn die Zahl eine endliche Anzahl von Ziffern hat Basis 10. – sepp2k

+0

http://floating-point-gui.de/ –

3

Das Problem ist, dass floating point is inaccurate. Sie können es lösen, indem Sie Rational, BigDecimal oder nur einfache Ganzzahlen verwenden (wenn Sie beispielsweise Geld speichern möchten, können Sie die Anzahl der Cents als Int speichern, anstatt die Anzahl der Dollars als Float).

BigDecimal kann genau jede Zahl mit einer endlichen Anzahl von Ziffern in der Basis 10 speichern und Rundungsnummern, die das nicht tun (also sind drei Drittel kein Ganzes).

Rational kann jede rationale Zahl genau speichern und kann keine irrationalen Zahlen speichern.

0

Da Sie Fließkomma-Mathematik ausführen, ist die zurückgegebene Zahl, was Ihr Computer für die Genauigkeit verwendet.

Wenn Sie eine genauere Antwort auf eine bestimmte Genauigkeit wünschen, multiplizieren Sie einfach den Gleitkommawert (z. B. mit 100), konvertieren Sie ihn in einen int, führen Sie die Berechnung durch und teilen Sie ihn dann.

Es gibt andere Lösungen, aber ich finde, dass dies die einfachste ist, da das Runden immer ein bisschen zweifelhaft für mich erscheint.

Dies hat vor hier gefragt worden sind, können Sie einige der Antworten vor gegeben aussehen wollen, wie diese: Dealing with accuracy problems in floating-point numbers

6

Das Problem ist, dass einige Zahlen, die wir einfach schreiben können in Dezimalzahlen haben keine genaue Darstellung in dem speziellen Fließkommaformat, das von aktueller Hardware implementiert wird. Eine lässige Art, dies zu sagen, ist, dass alle Ganzzahlen tun, aber nicht alle Brüche, weil wir normalerweise die Fraktion mit einem 2**e Exponenten speichern. Also, Sie haben 3 Möglichkeiten:

  1. Runden Sie entsprechend ab. Das nicht gerundete Ergebnis ist immer sehr nah, also ist ein rundes Ergebnis immer "perfekt". Dies ist, was Javascript tut und viele Leute bemerken nicht einmal, dass JS alles im Gleitkomma-Punkt erledigt.

  2. Verwenden Sie Festkommaarithmetik. Ruby macht das wirklich sehr leicht. Es ist eine der wenigen Sprachen, die von Fixnum nahtlos in die Klasse Bignum wechselt, wenn die Zahlen größer werden.

  3. eine Klasse verwenden, die dieses Problem zu lösen, wie BigDecimal

das Problem im Detail zu sehen konzipiert ist, können wir versuchen, Ihre „7.3“ in binär darzustellen. Der 7 Teil ist einfach, 111, aber wie machen wir .3? 111.1 ist 7.5, zu groß, 111.01 ist 7.25, kommt näher.Stellt sich heraus, 111.010011 ist die "nächstkleinere kleinere Zahl", 7.296875, und wenn wir versuchen, die fehlende .003125 auszufüllen, finden wir schließlich heraus, dass es nur 111.010011001100110011 ... für immer in unserer endlichen Bitfolge nicht darstellbar ist .

1

Es ist interessant zu bemerken, dass eine Zahl, die wenige Dezimalstellen in einer Basis hat, typischerweise eine sehr große Anzahl von Dezimalstellen in einer anderen haben kann. Zum Beispiel benötigt es eine unendliche Anzahl von Dezimalen, um 1/3 (= 0,3333 ...) in der Basis 10 auszudrücken, aber nur eine Dezimalstelle in der Basis 3. Ebenso braucht es viele Dezimalstellen, um die Zahl 1/10 auszudrücken (= 0,1) in der Basis 2.