Output BigDecimal anstatt double / Problem beim Rechnen

internet

Top Contributor
Ich bin gerade dabei eine Methode anstatt einem double einen BigDecimal zu verwenden.
Gerundet soll werden nach:

RoundingMode.HALF_EVEN
Scale = 6


LocalDateTime tempDate = 2020-11-13T00:00;
Local updateDate = 2020-11-14T11:06;
int totalMonths = 3;

Java:
long totalMinutes = Duration.between(tempDate, updateDate).toMillis() / 1000;
        long maxMinutes = YearMonth.of(tempDate.getYear(), tempDate.getMonthValue()).lengthOfMonth() * 24 * 60 * 60;
        result = (((double) totalMonths) / 12)
                + (((double) totalMinutes) / ((double) maxMinutes)
                        / 12));

Habe folgendes probiert:
Java:
BigDecimal c1 = (new BigDecimal(totalMonths)).divide(new BigDecimal(12), 6, RoundingMode.HALF_EVEN);
BigDecimal c2 = new BigDecimal(totalMinutes).divide(new BigDecimal(maxMinutes), 6, RoundingMode.HALF_EVEN);
BigDecimal c3 = new BigDecimal(12);
       
BigDecimal test = ((c1).add(c2)).divide(c3, 6, RoundingMode.HALF_EVEN);

Herauskommen soll: 0.2540625 (laut double)
Rausbekommen tue ich aber: 0.024896

Anschließend würde ich gerne den oberen Wert von 1 abziehen also:
Java:
double usedValue = Wert von oben;
result = 1-usedValue;

Womit dann 0.745937 rauskommen soll.

Jemand eine Idee, was ich falsch mache?
 
Java:
long totalMinutes = Duration.between(tempDate, updateDate).toMillis() / 1000;

Millisekunden dividiert durch tausend ergibt Sekunden, nicht Minuten.
 
Java:
long maxMinutes = YearMonth.of(tempDate.getYear(), tempDate.getMonthValue()).lengthOfMonth() * 24 * 60 * 60;

Und das sind Sekunden, nicht Minuten...und deine "double" Logik kuerzt sich auf:

Java:
result = (totalMonths / 12) + (totalMinutes / maxMinutes / 12);

Und da ist irgendwie eine Klammer zu viel.
 
Du hast geschrieben dass die Ergebnisse zwischen "double" und "BigDecimal" unterschiedlich sind...und deine Logiken sind unterschiedlich, in etwa um den Faktor 10. Deswegen hast du da ein unterschiedliches Ergebnis.
 
Du hast geschrieben dass die Ergebnisse zwischen "double" und "BigDecimal" unterschiedlich sind...und deine Logiken sind unterschiedlich, in etwa um den Faktor 10. Deswegen hast du da ein unterschiedliches Ergebnis.
wo meinst du, dass die Logik unterschiedlich ist:

Java:
  double result = (((double) totalMonths) / 12) + (((double) totalMinutes) / ((double) maxMinutes)/ 12);

Java:
    BigDecimal c1 = (new BigDecimal(totalMonths)).divide(new BigDecimal(12), 6, CalculateFunctions.ROUNDING_MODE);
        BigDecimal c2 = new BigDecimal(totalMinutes).divide(new BigDecimal(maxMinutes), 6, CalculateFunctions.ROUNDING_MODE);
        BigDecimal c3 = new BigDecimal(12);       
        BigDecimal result = ((c1).add(c2)).divide(c3, 6, CalculateFunctions.ROUNDING_MODE);

long totalMinutes und long maxMinutes nehme ich ja beides mal das gleiche...
 
Ja, genau da.

Java:
double result = (((double) totalMonths) / 12) + (((double) totalMinutes) / ((double) maxMinutes)/ 12);
BigDecimal result = ((c1).add(c2)).divide(c3, 6, CalculateFunctions.ROUNDING_MODE);

Vereinfacht:

Java:
double result = (totalMonths / 12) + ( totalMinutes / maxMinutes / 12);
BigDecimal result = c1.add(c2).divide(c3);
 
Ne, das stimmt etwas nicht...
Java:
double result1 = (totalMonths / 12) + ( totalMinutes / maxMinutes / 12);

Hier bekomme ich 0 raus, der Wert ist aber: 0.2540625 (und der ist korrekt)
 
Ja, weil ich es vereinfacht habe um dir zu zeigen dass die Klammernsetzung falsch ist. Du dividierst durch zwoelf und addierst dann, im Gegensatz zu "BigDecimal", wo du addierst und dann dividierst.
 
Ja, weil ich es vereinfacht habe um dir zu zeigen dass die Klammernsetzung falsch ist. Du dividierst durch zwoelf und addierst dann, im Gegensatz zu "BigDecimal", wo du addierst und dann dividierst.
wie gesagt, die obere Kalkulation double ist richtig:
Java:
result = (((double) totalMonths) / 12) + (((double) totalMinutes) / ((double) maxMinutes) / 12));
= 0.2540625

Bei der Vereinfachung:
Java:
double result1 = (totalMonths / 12) + ( totalMinutes / maxMinutes / 12);
= 0

Ich möchte als BigDecimal ebenfalls die 0.2540625
 
Edit: Hier dein Code mit BigDecimal
Java:
BigDecimal c1 = (new BigDecimal(totalMonths)).divide(new BigDecimal(12), 6, CalculateFunctions.ROUNDING_MODE);
BigDecimal c2 = new BigDecimal(totalMinutes).divide(new BigDecimal(maxMinutes), 6, CalculateFunctions.ROUNDING_MODE);
BigDecimal c3 = new BigDecimal(12);
BigDecimal result = ((c1).add(c2)).divide(c3, 6, CalculateFunctions.ROUNDING_MODE);

Vereinfacht:

c1 = totalMonths / 12
c2 = totalMinutes / maxMinutes
result = (c1 + c2) / 12 <== die Klammerung ergibt sich aus c1.add(c2) und das Ergebnis daraus wird durch 12 dividiert

c1 und c2 eingesetzt:

result = ( (totalMonths / 12) + (totalMinutes / maxMinutes) ) / 12 <== beachte das die Addition vor der Division ausgeführt wird

vs. dem Code mit double

result = (totalMonths / 12) + ( totalMinutes / maxMinutes / 12) <== hier werden erst die Divisionen und dann die Addition ausgeführt

Edit: Warum rundest du dreimal?
 
Zuletzt bearbeitet:
Java:
BigDecimal c1 = (new BigDecimal(totalMonths)).divide(new BigDecimal(12), 6, CalculateFunctions.ROUNDING_MODE);
BigDecimal c2 = new BigDecimal(totalMinutes).divide(new BigDecimal(maxMinutes), 6, CalculateFunctions.ROUNDING_MODE);
BigDecimal c3 = new BigDecimal(12);  
BigDecimal result = ((c1).add(c2)).divide(c3, 6, CalculateFunctions.ROUNDING_MODE);
hier bekomme ich dann aber das raus: 0.024896 🙄
 
Was das ganz deutlich zeigt: der Code ist so unleserlich, dass du ihn selbst nicht mehr richtig lesen kannst.

Diesen Zustand musst du ändern!

Und um das richtig zu stellen: Du willst erst c2 / c3 rechnen und das Ergebnis dann zu c1 addieren ...

Also c1.add(c2.divide(c3)) um es einmal deutlich zu schreiben. Aber wenn Du es nicht lesen kannst, dann musst Du es weiter unterteilen, also von mir aus:
c4 = c2.divide(c3)
result = c1.add(c4)
oder so ...

Und c1, c2, c3, ... sind Namen, die Du dir abgewöhnen musst. Was berechnest Du da? Was wird da summiert? Etwas sinnvollere Namen helfen auch dabei, Code lesbar zu machen!
 
Evtl. ein paar kleine Hinweise bezüglich Lesbarkeit:
Das Unterteilen auf mehrere Zeilen hast Du ja schon selbst etwas gemacht. Dabei fällt aber massiv auch die Menge der verwendeten Klammern auf... Die machen es extrem unleserlich.

Hier hilft dann teilweise:
a) Natürlich ein fundiertes Wissen von Java so dass man weiss, wo ein Cast notwendig ist und wo nicht und dann auch, wo welche Klammer unnötig ist. So läßt sich ein Ausdruck wie der bei der Berechnung mit double schon deutlich verbessern.
b) Ausdruck über mehrere Zeilen kann helfen (Hier sehe ich das aber nicht wirklich). Du hast also sowas wie f1(a1, a2) wobei a1 und a2 Ausdrücke sind, die evtl. nicht ganz so übersichtlich sind. Das kann mann dann schreiben als
Java:
f1(
    a1,
    a2
);
Die Ausdrücke a1 und a2 sind dann jeweils auf einer eigenen Zeile und sind so als Einheit zu erkennen.

Aber das wirklich wichtige Hilfsmittel:
c) Unterteile in Methoden. Also Monate durch 12 sind Jahre -> Das kann in eine Methode...
Java:
BigDezimal berechneJahreAusMonaten(final long monate) {
    return new BigDezimal(monate).divide(BigDezimal.valueOf(12), 6, CalculateFunctions.ROUNDING_MODE);
}
Bei dem Rest, weiss ich nicht, was Du da genau berechnest ... aber da muss ja auch irgendwie Jahre rauskommen, denn Du addierst das ja ... Aber egal - mir geht es nicht um Deine Logik sondern nur um Hinweise zum lesbar machen ...

Am Ende solltest Du kurze Methoden haben, die gut lesbar sind. Dann steht da etwas wie:
Java:
berechneWasAuchImmer(final long monate, final long minuten, final long maxMinuten) {
    return berechneJahreAusMonaten(monate).add(berechneJahreAusMinutenUndMaxMinuten(minuten, maxMinuten));
}
Das nur also Beispiel ... bei den Namen fehlt mir jeder Bezug zu dem Kontext, in dem Du bist. Aber vielleicht ist das ja deutlich geworden...

So extrem kurz muss es auch nicht bleiben. Aber es muss immer deutlich sein:
Java:
public void starteAuto(final Schluessel schluessel) {
    zuendschloss.add(schluessel);
    if (!schaltung.istInNeutralerStellung()) {
        schaltung.schalteNeutraleStellung();
    }
    
    zuendschloss.zuendungEin();
    checkKontrollleuchten();
    zuendschloss.anlasserLeuft();
    warteMotorStartOderTimeout();
    zuendschloss.zuendungEin();
}

Sowas kann man dann doch gut lesen, oder? Da sind dann auch Fehler leichter zu finden.Und etwas in der Art sollte durchaus das Ziel sein.

Das einfach nur als kleine Erläuterung. Mit der bin ich weiter sehr grob und oberflächig geblieben, aber ich hoffe, dass es etwas hilft, die Richtung, in die es sich etwas entwickeln sollte, zu verstehen.
 
Wie wär's denn, wenn du die Berechnung in eine Methode packst, dann genau die gleiche Methode (identischer Code) für den anderen Datentyp erstellst.
Dann musst du gegebenenfalls nur noch beim Aufruf casten.
 

Zurück
Oben