Frage

    

Diese Frage bereits eine Antwort hier:

    
            
  •              beibehalten Präzision mit Doppel in Java                                      20 Antworten                          
  •     
    

Scheint, wie die Subtraktion auslöst irgendeine Art von Problem und der resultierende Wert ist falsch.

double tempCommission = targetPremium.doubleValue()*rate.doubleValue()/100d;

78,75 = 787,5 * 10,0 / 100d

double netToCompany = targetPremium.doubleValue() - tempCommission;

708,75 = 787,5-78,75

double dCommission = request.getPremium().doubleValue() - netToCompany;

877,8499999999999 = 1586,6-708,75

Der resultierende Erwartungswert wäre 877,85.

Was sollte getan wird die korrekte Berechnung zu gewährleisten?

War es hilfreich?

Lösung

Um die Genauigkeit der Gleitpunktarithmetik zu steuern, sollten Sie a href verwenden <= "http://java.sun.com/javase/6/docs/api/java/math/BigDecimal.html" rel = "noreferrer „> java.math.BigDecimal . Lesen Sie Die Notwendigkeit für BigDecimal von John Zukowski für weitere Informationen.

Bei Ihrem Beispiel wäre die letzte Zeile wie seine BigDecimal folgenden verwendet wird.

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf("1586.6");
BigDecimal netToCompany = BigDecimal.valueOf("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

Daraus ergibt sich die folgende Ausgabe.

877.85 = 1586.6 - 708.75

Andere Tipps

Wie die bisherigen Antworten angegeben, dies ist eine Folge der Gleitkomma-Arithmetik zu tun.

Als vorher bereits vorgeschlagen, wenn Sie numerische Berechnungen tun, verwendet java.math.BigDecimal.

Es gibt jedoch eine Gotcha zu verwenden BigDecimal. Wenn Sie von dem doppelten Wert auf eine BigDecimal konvertieren, haben Sie die Wahl eines neuen BigDecimal(double) Konstruktor oder die BigDecimal.valueOf(double) statische Factory-Methode zu verwenden. Verwenden Sie die statische Factory-Methode.

Der doppelte Konstruktor die gesamte Genauigkeit der double zu einem BigDecimal umwandelt, während die statische Fabrik wandelt sie effektiv auf einen String, dann wandelt diese in einem BigDecimal.

Dies wird relevant, wenn Sie in diese subtilen Rundungsfehler ausgeführt werden. Eine Reihe könnte als 0,585 angezeigt, aber intern sein Wert ‚0,58499999999999996447286321199499070644378662109375‘. Wenn Sie den BigDecimal Konstruktor verwendet wird, würden Sie die Nummer erhalten, die nicht gleich 0,585 ist, während die statische Methode würden Sie gleich 0.585 Wert geben.

double value = 0.585;
System.out.println(new BigDecimal(value));
System.out.println(BigDecimal.valueOf(value));

auf meinem System gibt

0.58499999999999996447286321199499070644378662109375
0.585

Ein weiteres Beispiel:

double d = 0;
for (int i = 1; i <= 10; i++) {
    d += 0.1;
}
System.out.println(d);    // prints 0.9999999999999999 not 1.0

Verwenden BigDecimal statt.

EDIT:

Auch nur darauf zu hinweisen, diese Rundung Frage keine ‚Java‘ ist. Andere Sprachen Ausstellung ähnlich (wenn auch nicht unbedingt konsistent) Verhalten. Java zumindest gewährleistet ein konsistentes Verhalten in dieser Hinsicht.

Ich würde das obige Beispiel wie folgt ändern:

import java.math.BigDecimal;

BigDecimal premium = new BigDecimal("1586.6");
BigDecimal netToCompany = new BigDecimal("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

Auf diese Weise kann die Gefahren der Verwendung von String vermeiden zu beginnen. Eine weitere Alternative:

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf(158660, 2);
BigDecimal netToCompany = BigDecimal.valueOf(70875, 2);
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

Ich denke, diese Optionen sind besser als verdoppelt werden. In Webapps Startnummern sowieso als Strings aus.

Jedes Mal, wenn Sie Berechnungen mit Doppel tun, kann das passieren. Dieser Code würden Sie 877,85:

Doppel Antwort = Math.round (dCommission * 100000) / 100000.0;

Speichern Sie die Anzahl von Cent anstatt Dollar, und tun nur das Format Dollar bei der Ausgabe es. Auf diese Weise können Sie eine ganze Zahl verwenden, die von den Präzisions Probleme nicht leiden.

Siehe Antworten href="https://stackoverflow.com/questions/178952/adding-floats-with-gmp-gives-correct-results-sort-of"> diese Frage

Sie könnten einige beliebige Genauigkeit wählen (signifikante Ziffern Ihrer Eingaben?) Und runden Sie Ihr Ergebnis, um es, wenn Sie sich wohl fühlen, das zu tun.

Das ist ein lustiges Thema.

Die Idee hinter Timons Antwort ist, dass Sie ein Epsilon angeben, welche die kleinste Präzision kann eine rechtliche Doppel darstellt. Wenn Sie in Ihrer Anwendung kennen, die Sie nie dann Präzision unter 0.00000001 benötigt, was er vorschlägt, ist ausreichend, um ein genaueres Ergebnis sehr nahe an die Wahrheit zu bekommen. Nützlich bei Anwendungen, bei denen sie wissen, vorne ihre maximale Genauigkeit (in Beispiel Finanzierung Währung Genauigkeiten usw.)

Doch das grundlegende Problem mit dem Versuch, es abzurunden, ist, dass, wenn Sie um einen Faktor dividieren sie neu zu skalieren Sie eine andere Möglichkeit für Präzisions Probleme tatsächlich einzuführen. Jede Manipulation Doppel kann Unschärfen Probleme einführen mit unterschiedlicher Frequenz. Vor allem, wenn Sie mit einer sehr signifikanten Stelle zu runden sind versucht (so Ihre Operanden <0) zum Beispiel, wenn Sie den folgenden mit Timons Code ausführen:

System.out.println(round((1515476.0) * 0.00001) / 0.00001);

Wird in 1499999.9999999998 führen, wo das Ziel ist hier an den Einheiten von 500000 abzurunden (das heißt wir wollen 1500000)

In der Tat der einzige Weg, ganz sicher zu sein, um Ihnen die Ungenauigkeit eliminiert haben, ist durch ein BigDecimal gehen zu skalieren. z.

System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue());

eine Mischung aus der Epsilon Strategie und der BigDecimal Strategie wird Ihnen eine genaue Kontrolle über Ihre Präzision. Die Idee, das Epsilon ist bekommt man ganz in der Nähe und dann die BigDecimal werden jede Ungenauigkeit von rescaling danach, verursacht werden. Obwohl mit BigDecimal würde die erwartete Leistung der Anwendung reduzieren.

Es wurde mich darauf hingewiesen, dass der letzte Schritt BigDecimal des Verwendens es neu zu skalieren ist nicht immer notwendig, für einige Fälle verwendet, wenn Sie feststellen können, dass es kein Eingabewert, dass die endgültige Teilung einen Fehler wieder einführen kann. Derzeit weiß ich nicht, wie man richtig, dies bestimmen also, wenn jemand weiß, wie dann würde ich mich freuen, davon zu hören.

Bisher ist die eleganteste und effizienteste Art und Weise, in Java zu tun:

double newNum = Math.floor(num * 100 + 0.5) / 100;

Noch besser wäre es verwenden JScience als BigDecimal ziemlich begrenzt ist (zB keine sqrt Funktion)

double dCommission = 1586.6 - 708.75;
System.out.println(dCommission);
> 877.8499999999999

Real dCommissionR = Real.valueOf(1586.6 - 708.75);
System.out.println(dCommissionR);
> 877.850000000000
double rounded = Math.rint(toround * 100) / 100;

Auch wenn Sie nicht verdoppelt für präzise Berechnungen der folgende Trick half mir verwenden sollten, wenn Sie die Ergebnisse ohnehin runden.

public static int round(Double i) {
    return (int) Math.round(i + ((i > 0.0) ? 0.00000001 : -0.00000001));
}

Beispiel:

    Double foo = 0.0;
    for (int i = 1; i <= 150; i++) {
        foo += 0.00010;
    }
    System.out.println(foo);
    System.out.println(Math.round(foo * 100.0) / 100.0);
    System.out.println(round(foo*100.0) / 100.0);

Welche druckt:

0.014999999999999965
0.01
0.02

Mehr Infos: http://en.wikipedia.org/wiki/Double_precision

Es ist ganz einfach.

Mit dem% .2f Operator für die Ausgabe. Problem gelöst!

Zum Beispiel:

int a = 877.8499999999999;
System.out.printf("Formatted Output is: %.2f", a);

Der obige Code führt zu einer Druckausgabe von: 877,85

Das% .2f Operator legt fest, dass nur zwei Dezimalstellen verwendet werden sollen.

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