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