public class doublePrecision {
public static void main(String[] args) {
double total = 0;
total += 5.6;
total += 5.8;
System.out.println(total);
}
}
Der obige Code wird gedruckt:
11.399999999999
Wie würde ich dies dazu bringen, nur 11.4 zu drucken (oder als verwenden zu können)?
java
floating-point
double
precision
Deinumite
quelle
quelle
Antworten:
Wie andere bereits erwähnt haben, möchten Sie wahrscheinlich die
BigDecimal
Klasse verwenden, wenn Sie eine genaue Darstellung von 11.4 wünschen.Nun eine kleine Erklärung, warum dies geschieht:
Die
float
unddouble
primitiven Typen in Java sind Gleitkommazahlen , wobei die Zahl als binäre Darstellung eines Bruchs und eines Exponenten gespeichert wird.Insbesondere ist ein Gleitkommawert mit doppelter Genauigkeit wie der
double
Typ ein 64-Bit-Wert, wobei:Diese Teile werden kombiniert, um eine
double
Darstellung eines Wertes zu erzeugen .(Quelle: Wikipedia: Doppelte Präzision )
Eine ausführliche Beschreibung des Umgangs mit Gleitkommawerten in Java finden Sie in Abschnitt 4.2.3: Gleitkommatypen, -formate und -werte der Java-Sprachspezifikation.
Die
byte
,char
,int
,long
Typen sind Festkommazahlen, die genauen representions von Zahlen sind. Im Gegensatz zu Festkommazahlen können Gleitkommazahlen manchmal (sicher "meistens") keine genaue Darstellung einer Zahl zurückgeben. Dies ist der Grund, warum Sie11.399999999999
als Ergebnis von enden5.6 + 5.8
.Wenn Sie einen genauen Wert benötigen, z. B. 1,5 oder 150,1005, sollten Sie einen der Festkommatypen verwenden, der die Zahl genau darstellen kann.
Wie bereits mehrfach erwähnt, verfügt Java über eine
BigDecimal
Klasse, die sehr große und sehr kleine Zahlen verarbeiten kann.Aus der Java-API-Referenz für die
BigDecimal
Klasse:Es gab viele Fragen zum Stapelüberlauf in Bezug auf Gleitkommazahlen und deren Genauigkeit. Hier ist eine Liste verwandter Fragen, die von Interesse sein können:
Wenn Sie wirklich auf die Details von Gleitkommazahlen eingehen möchten, schauen Sie sich an, was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte .
quelle
BigDecimal
viel langsamer alsdouble
in diesem Fall ist, aber nicht benötigt wird, da double 15 Dezimalstellen Genauigkeit hat, sondern nur gerundet werden muss.Wenn Sie beispielsweise eine doppelte Zahl eingeben,
33.33333333333333
ist der Wert, den Sie erhalten, tatsächlich der am nächsten darstellbare Wert mit doppelter Genauigkeit, und zwar genau:Wenn Sie das durch 100 teilen, erhalten Sie:
Dies ist auch nicht als Zahl mit doppelter Genauigkeit darstellbar. Daher wird es erneut auf den nächsten darstellbaren Wert gerundet. Dies ist genau:
Wenn Sie diesen Wert ausdrucken, wird er erneut auf 17 Dezimalstellen gerundet , was Folgendes ergibt:
quelle
Wenn Sie nur Werte als Brüche verarbeiten möchten, können Sie eine Bruchklasse erstellen, die ein Zähler- und Nennerfeld enthält.
Schreiben Sie Methoden zum Addieren, Subtrahieren, Multiplizieren und Dividieren sowie eine toDouble-Methode. Auf diese Weise können Sie Floats während der Berechnungen vermeiden.
EDIT: Schnelle Implementierung,
quelle
numerator
unddenominator
sollteint
s sein? Warum sollten Sie Gleitkommapräzision wünschen?Beachten Sie, dass Sie das gleiche Problem haben würden, wenn Sie eine Dezimalarithmetik mit begrenzter Genauigkeit verwenden und 1/3 behandeln möchten: 0,333333333 * 3 ist 0,999999999, nicht 1,00000000.
Leider sind 5.6, 5.8 und 11.4 keine runden Zahlen in Binärform, da es sich um Fünftel handelt. Die Float-Darstellung von ihnen ist also nicht genau, genauso wie 0,3333 nicht genau 1/3 ist.
Wenn alle von Ihnen verwendeten Zahlen einmalige Dezimalstellen sind und Sie genaue Ergebnisse wünschen, verwenden Sie BigDecimal. Oder wie andere gesagt haben, wenn Ihre Werte wie Geld in dem Sinne sind, dass sie alle ein Vielfaches von 0,01 oder 0,001 oder so sind, dann multiplizieren Sie alles mit einer festen Potenz von 10 und verwenden Sie int oder long (Addition und Subtraktion sind trivial: auf Multiplikation achten).
Wenn Sie jedoch mit Binärdaten für die Berechnung zufrieden sind, aber nur Dinge in einem etwas freundlicheren Format ausdrucken möchten, versuchen Sie es mit
java.util.Formatter
oderString.format
. Geben Sie in der Formatzeichenfolge eine Genauigkeit an, die geringer ist als die volle Genauigkeit eines Doppels. Bei 10 signifikanten Zahlen ist beispielsweise 11.399999999999 11.4, sodass das Ergebnis in Fällen, in denen das binäre Ergebnis einem Wert sehr nahe kommt, der nur wenige Dezimalstellen erfordert, fast genauso genau und besser lesbar ist.Die Genauigkeit der Angabe hängt ein wenig davon ab, wie viel Mathematik Sie mit Ihren Zahlen durchgeführt haben. Je mehr Sie tun, desto mehr Fehler werden im Allgemeinen akkumuliert, aber einige Algorithmen akkumulieren sie viel schneller als andere (sie werden als "instabil" bezeichnet) im Gegensatz zu "stabil" in Bezug auf Rundungsfehler). Wenn Sie nur ein paar Werte hinzufügen, wird das Löschen von nur einer Dezimalstelle die Genauigkeit verbessern. Experiment.
quelle
Vielleicht möchten Sie die Verwendung der Java-Klasse java.math.BigDecimal untersuchen, wenn Sie wirklich Präzisionsmathematik benötigen. Hier ist ein guter Artikel von Oracle / Sun zum Fall BigDecimal . Sie können zwar niemals 1/3 darstellen, wie jemand erwähnt hat, aber Sie können die Leistung genau zu entscheiden , wie genau Sie das Ergebnis sein soll. setScale () ist dein Freund .. :)
Ok, weil ich im Moment viel zu viel Zeit habe, ist hier ein Codebeispiel, das sich auf Ihre Frage bezieht:
und um meine neue Lieblingssprache, Groovy, anzuschließen, hier ist ein besseres Beispiel für dasselbe:
quelle
Ich bin mir ziemlich sicher, dass Sie das zu einem dreizeiligen Beispiel hätten machen können. :) :)
Wenn Sie genaue Genauigkeit wünschen, verwenden Sie BigDecimal. Andernfalls können Sie Ints multipliziert mit 10 ^ verwenden, was auch immer Sie wollen.
quelle
Wie andere angemerkt haben, können nicht alle Dezimalwerte als binär dargestellt werden, da die Dezimalzahl auf Potenzen von 10 und die Binärzahl auf Potenzen von zwei basiert.
Wenn Präzision wichtig ist, verwenden Sie BigDecimal, aber wenn Sie nur eine benutzerfreundliche Ausgabe wünschen:
Werde dir geben:
quelle
Sie stoßen auf die Genauigkeitsbeschränkung vom Typ double.
Java.Math verfügt über einige arithmetische Funktionen mit beliebiger Genauigkeit.
quelle
Sie können nicht, weil 7.3 keine endliche Darstellung in Binärform hat. Der nächstgelegene ist 2054767329987789/2 ** 48 = 7,3 + 1/1407374883553280.
Schauen Sie sich http://docs.python.org/tutorial/floatingpoint.html anWeitere Informationen finden . (Es ist auf der Python-Website, aber Java und C ++ haben das gleiche "Problem".)
Die Lösung hängt davon ab, was genau Ihr Problem ist:
quelle
quelle
Verwenden Sie java.math.BigDecimal
Doubles sind intern binäre Brüche, daher können sie manchmal keine Dezimalbrüche auf die exakte Dezimalzahl darstellen.
quelle
Multiplizieren Sie alles mit 100 und lagern Sie es in Cent.
quelle
Computer speichern Zahlen in Binärform und können Zahlen wie 33.333333333 oder 100.0 nicht genau darstellen. Dies ist eines der kniffligen Dinge bei der Verwendung von Doppel. Sie müssen die Antwort nur abrunden, bevor Sie sie einem Benutzer anzeigen. Glücklicherweise benötigen Sie in den meisten Anwendungen sowieso nicht so viele Dezimalstellen.
quelle
Gleitkommazahlen unterscheiden sich von reellen Zahlen darin, dass es für jede gegebene Gleitkommazahl eine nächsthöhere Gleitkommazahl gibt. Gleich wie ganze Zahlen. Es gibt keine ganze Zahl zwischen 1 und 2.
Es gibt keine Möglichkeit, 1/3 als Float darzustellen. Es gibt einen Schwimmer darunter und einen Schwimmer darüber, und es gibt einen gewissen Abstand zwischen ihnen. Und 1/3 ist in diesem Raum.
Apfloat für Java behauptet, mit Gleitkommazahlen mit beliebiger Genauigkeit zu arbeiten, aber ich habe es nie verwendet. Wahrscheinlich einen Blick wert. http://www.apfloat.org/apfloat_java/
Eine ähnliche Frage wurde hier vor der hochpräzisen Java-Gleitkomma-Bibliothek gestellt
quelle
Doubles sind Annäherungen an die Dezimalzahlen in Ihrer Java-Quelle. Sie sehen die Konsequenz der Nichtübereinstimmung zwischen dem Double (das ein binär codierter Wert ist) und Ihrer Quelle (die dezimal codiert ist).
Java erzeugt die nächste binäre Näherung. Mit java.text.DecimalFormat können Sie einen besser aussehenden Dezimalwert anzeigen.
quelle
Verwenden Sie ein BigDecimal. Sie können sogar Rundungsregeln angeben (z. B. ROUND_HALF_EVEN, mit denen statistische Fehler minimiert werden, indem auf den geraden Nachbarn gerundet wird, wenn beide den gleichen Abstand haben, dh sowohl 1,5 als auch 2,5 auf 2 runden).
quelle
Kurze Antwort: Verwenden Sie immer BigDecimal und stellen Sie sicher, dass Sie den Konstruktor mit dem String- Argument verwenden, nicht das doppelte.
Zurück zu Ihrem Beispiel: Der folgende Code gibt 11.4 aus, wie Sie möchten.
quelle
Schauen Sie sich BigDecimal an, es behandelt Probleme, die mit einer solchen Gleitkomma-Arithmetik zu tun haben.
Der neue Anruf würde folgendermaßen aussehen:
Verwenden Sie setScale (), um die Anzahl der zu verwendenden Dezimalstellen festzulegen.
quelle
Warum nicht die round () -Methode aus dem Mathematikunterricht verwenden?
quelle
Wenn Sie keine andere Wahl haben, als doppelte Werte zu verwenden, können Sie den folgenden Code verwenden.
quelle
Verschwenden Sie Ihren Efford nicht mit BigDecimal. In 99,99999% der Fälle benötigen Sie es nicht. java Doppeltyp ist von cource annähernde , jedoch in fast allen Fällen ist es hinreichend präzise. Beachten Sie, dass bei der 14. signifikanten Stelle ein Fehler vorliegt.Das ist wirklich vernachlässigbar!
Um eine schöne Ausgabe zu erhalten, verwenden Sie:
quelle