2 Stimmen

Java verdoppelt das Hinzufügen auf seltsame Weise (und nein, es geht nicht um Geld)

Dies ist also der einzige relevante Abschnitt des Codes

System.out.println("first term of " + firstTerm +
                   " second term of " + secondTerm + 
                   " third term of " + finalTermHolder + 
                   " should equal " + oppositeIntHolder);
double holder = firstTerm + secondTerm + finalTermHolder;
System.out.println(holder + " should equal " + oppositeIntHolder);

Es handelt sich um ununterbrochenen Code, dazwischen gibt es nichts. Die Ausgabe für das erste println ist:

first term of 2.5147186257614296 second term of -9.514718625761429 third term of 7.0 should equal 0.0

Das zweite println ergibt:

8.881784197001252E-16 should equal 0.0

Warum ergeben -9,5, 2,5 und 7 in der Summe 8,9 statt 0?

4voto

andrew cooke Punkte 43891

Sie ergeben nicht 8,9. sie ergeben 8,9e-16. das sind etwa 0,00000000000000089

selbst wenn die Zahlen als -9,5 usw. angezeigt würden, könnte man das immer noch sehen. das liegt daran, dass Binärcomputer Dezimalzahlen nicht exakt speichern. es treten kleine Fehler auf. und ja, das ist genau das Problem, das beim Geld auftritt.

2voto

Shawn Punkte 7065

8,881784197001252E-16 ist viel näher an Null als man denkt ; )

Double ist in Java eine Fließkommazahl. WENN Sie nach einer exakten Darstellung der Zahl suchen, versuchen Sie es mit BigDecimal anstelle von Double

BigDecimal num1 = new BigDecimal(3.32);
BigDecimal num2 = new BigDecimal(3.68);
System.out.println(num1.add(num2)); //will output 7.0

0voto

Peter Lawrey Punkte 511323

Wenn Sie doppelte Operationen durchführen, müssen Sie für eine angemessene Rundung sorgen. Auch bei der BigDecimal-Division müssen Sie für eine angemessene Rundung sorgen.

Zum Drucken double Es wird ein wenig gerundet, damit Sie den Darstellungsfehler nicht sehen. Nach ein paar Berechnungen (es ist nur eine erforderlich) ist der Rundungsfehler jedoch zu groß, und Sie können den Fehler sehen.

Wenn Sie die Darstellung und Rundungsfehler sehen wollen, verwenden Sie BigDecimal, da es eine exakte Konvertierung von double durchführt. Etwas, das an sich schon überraschend sein kann.

Übrigens gibt es bei einfachen Potenzen von 2 keinen Rundungsfehler. -9,5 + 2,5 + 7,0 ist also immer 0,0. Rundungsfehler gibt es nur bei anderen Dezimalzahlen wie 0,1

double[] ds = {
        0.1,
        0.2,
        -0.3,
        0.1 + 0.2 - 0.3};
for (double d : ds) {
    System.out.println(d + " => " + new BigDecimal(d));
}

druckt

0.1 => 0.1000000000000000055511151231257827021181583404541015625
0.2 => 0.200000000000000011102230246251565404236316680908203125
-0.3 => -0.299999999999999988897769753748434595763683319091796875
5.551115123125783E-17 => 5.5511151231257827021181583404541015625E-17

Sie können sehen, dass die Darstellung für 0,1 und 0,2 etwas höher ist als diese Werte, und -0,3 ist auch etwas höher. Wenn Sie sie ausdrucken, erhalten Sie die schönere 0,1 anstelle des tatsächlich dargestellten Wertes 0,1000000000000000055511151231257827021181583404541015625

Wenn man diese Werte jedoch addiert, erhält man einen Wert, der etwas höher als 0 ist.

Um dieses Problem zu lösen, müssen Sie für eine angemessene Rundung sorgen. Bei Geld ist dies einfach, da man weiß, wie viele Dezimalstellen angemessen sind, und solange man nicht 70 Billionen Dollar hat, wird man keinen Rundungsfehler bekommen, der groß genug ist, dass man ihn nicht korrigieren kann.

public static double roundToTwoPlaces(double d) {
    return ((long) (d < 0 ? d * 100 - 0.5 : d * 100 + 0.5)) / 100.0;
}

Wenn man dies zum Ergebnis hinzufügt, gibt es immer noch einen kleinen Darstellungsfehler, der jedoch nicht so groß ist, dass Double.toString(d) ihn nicht korrigieren kann.

double[] ds = {
        0.1,
        0.2,
        -0.3,
        0.1 + 0.2 - 0.3};
for (double d : ds) {
    System.out.println(d + " to two places " + roundToTwoPlaces(d) + " => " + new BigDecimal(roundToTwoPlaces(d)));
}

druckt

0.1 to two places 0.1 => 0.1000000000000000055511151231257827021181583404541015625
0.2 to two places 0.2 => 0.200000000000000011102230246251565404236316680908203125
-0.3 to two places -0.3 => -0.299999999999999988897769753748434595763683319091796875
5.551115123125783E-17 to two places 0.0 => 0

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X