BigDecimal und Berechnungen weiterhin mit + / - usw.

internet

Top Contributor
Hallo,

ich bin gerade dabei BigDecimal zu nutzen (davor habe ich immer nur double oder float) verwendet....
Ich verstehe, dass es Funktionen wie add, multiple etc. gibt.

Gerne möchte ich aber weiterhin mit +, -, / usw. rechnen (jedenfalls bei komplereren Berechnungen), da das sonst ziemlich unübersichtlich wird.
Also anstatt:
Java:
position.getPriceTotalNet().multiply(new BigDecimal(1.00).add(subPosition.getTax().divide(new BigDecimal(100.00)))));

Möchte ich:
Java:
new BigDecimal(subPosition.getPriceTotalNet().doubleValue * (1.00 + (subPosition.getTax().doubleValue / 100.00))));

Ich bin mir nun aber nicht sicher, ob das der richtige Ansatz ist?
 
Also wenn Du eh mit double Werten rechnest und alles immer in double Werte umwandelst: Dann bleib bei double und vergiss BigDecimal.

Wenn Du BigDecimal nutzen willst, dann nutz die Methoden von BigDecimal.

Das ist wie: Du hast ein Tolles Auto, aber das nutzt Du nie, denn die Methoden von Auto willst Du nicht nutzen. Statt dessen möchtest Du auf Deine Beine vertrauen, daher steigst Du immer aus und gehst zu Fuß, wenn Du irgendwo hin willst.
 
Also wenn Du eh mit double Werten rechnest und alles immer in double Werte umwandelst: Dann bleib bei double und vergiss BigDecimal.

Wenn Du BigDecimal nutzen willst, dann nutz die Methoden von BigDecimal.

Das ist wie: Du hast ein Tolles Auto, aber das nutzt Du nie, denn die Methoden von Auto willst Du nicht nutzen. Statt dessen möchtest Du auf Deine Beine vertrauen, daher steigst Du immer aus und gehst zu Fuß, wenn Du irgendwo hin willst.
Ich möche zu BigDecimal wechseln aus diesem Grund:

Was ich aber nicht möchte, ist, dass ich alle meine bestehenden Rechnungen nun mit den Funktionen abbilden muss.
 
Simple Lösung:
Keine komplexen Berechnungen in eine Zeile sondern in entsprechende Methode auslagern und dort auf mehrere Zeilen splitten.

Denn ganz ehrlich
Java:
new BigDecimal(subPosition.getPriceTotalNet().doubleValue * (1.00 + (subPosition.getTax().doubleValue / 100.00))));
Ist ist auch nicht wirklich lesbar. Ich kann dann nicht wirklich sofort sehen ob das was da berechnet wird, korrekt ist
 
Ich bin mir nun aber nicht sicher, ob das der richtige Ansatz ist?
Nein. Erstens musst Du den Betrag auf 2 Nkst runden und selbst dann erhältst Du z. B. für 0,5 € unterschiedliche Ergebnisse:

Java:
new BigDecimal(0.5 * (1.00 + 19.0/100.0)).setScale(2, BigDecimal.ROUND_HALF_UP)
liefert 0,59, während
Java:
new BigDecimal(0.5).multiply(BigDecimal.ONE.add(new BigDecimal(19).movePointLeft(2))).setScale(2, BigDecimal.ROUND_HALF_UP)
korrekte 0,60 liefert.
 
Nochmal was ausführlicher - ich kann deine Schmerzen durchaus nachvollziehen, ich bin auch in einem Bereich tätig, wo wir Beträge berechnen müssen (und im aktuellen Projekt sind die Berechnungen durchaus komplex). Eine Teilberechnung:

(1+A+i) / (((1-(p + s + g))*(1-r)*(1-f))

Schreibe ich das mit double in eine Zeile ist schon schwer lesbar.
Mit BigDecimal wäre es komplett unleserlich.

Wie sieht es mit BigDecimal aus der Code (bzw. die Klasse heißt bei uns Decimal und ist ein Wrapper um BigDecimal mit ein paar convience Eigenschaften)

Java:
        Decimal dividend = ONE.add(getA()).add(getI());
        Decimal divisorFaktor1 = ONE.subtract(getP().add(getS())
                .add(getG()));
        Decimal divisorFaktor2 = ONE.subtract(getR());
        Decimal divisorFaktor3 = ONE.subtract(getF());
        return getBerechnungsHelper().dividiere(dividend,
                                                divisorFaktor1.multiply(divisorFaktor2).multiply(divisorFaktor3));
Ja, es werden so deutlich mehr Zeilen - dafür ist es aber dann lesbarer

Anmerkung - die Variablen heißen nicht wirklich a,i etc - die haben schon sprechende Namen, aber ich kann hier keinen Business-Code 1:1 reinkopieren 🙂

Der BerechnungsHelper ist eine Utility-Klasse, die so Sachen wie Rundungen und Genauigkeiten wegkaspelt - wir haben z.B. definiert, dass die internen Berechnungen standardmäßig mit 5 Nachkommastellen und Rundungsmethode X gemacht werden. Und am Ende, der finale Wert wird dann auf 2 Nachkommastellen gerundet. Damit muss ich mir nicht bei jeder Berechnung Gedanken um die Genauigkeit machen, sondern die ist zentral definiert und kann auch zentral angepasst werden.
 
Zuletzt bearbeitet:
Also Java bietet kein operator overloading. Daher kannst Du nicht mit Instanzen von Klassen arbeiten ohne auf die Methoden zu wechseln.
In dem Moment, wo Du die Klasse nicht mehr verwendest und statt dessen die primären Typen von Java verwendest, dann bekommst Du sofort die Nachteile, die Du umgehen wolltest.

Als eine weitere Alternative wäre ggf. noch zu nennen: Nutze statt Java eine Sprache, die Operator Overloading unterstützt. Also z.B. Kotlin (so Du in der JVM bleiben möchtest) oder natürlich C++ und co ...

Aber in Java bleibt es bei dem friss oder stirb: Nutze Klassen mit den Methoden oder nutze die primären Datentypen zusammen mit den Operatoren. Eine andere Möglichkeit hast Du nicht und einen "Tod" musst Du hier "sterben" so Du bei Java bleiben willst.
 
Wie ich im anderen Thread schon sagte: du wirst vermutlich irgendwann den BigDecimal in einer extra "Geld"-Klasse kapseln wollen, die dir solche Operationen einfacher macht 🙂 Für Geldbeträge braucht man ja ein anderes Set an Operationen als für "Zahlen".


Um aber auch zu deiner ursprünglichen Formel was zu sagen: Prozentwerte wie zB "19%" speichert man sinnvollerweise nicht als 19, sondern als 0.19 – das ist der mathematische Wert dahinter, die "19%" sind nur eine einfache Darstellung für Menschen.

Wenn man die Rechnung dann noch etwas auseinander zieht, sieht das dann etwa so aus:
Java:
var totalTax = position.getPriceTotalNet().multiply(subPosition.getTax());
var totalPrice = position.getPriceTotalNet().add(totalTax);

was schon mal deutlich schöner aussieht, vor allem wenn man es noch in einer sinnvoll benannten Methode kapselt 🙂

.divide(new BigDecimal(100.00))
Ersetzen durch .movePointLeft(2).
Ich bin ehrlich überrascht, dass du movePointLeft leserlicher findest als divide 😅
 
Um auch nochmal Senf dazuzugeben, falls dir die Kommastellen nach den Centstellen (also Bruchteile von Cents) egal sind, würde ich darauf zurückgreifen, Geldbeträge in Cent als int zu speichern. Wie gesagt, funktioniert nur, wenn Bruchteile von Cents in einer Operation auf Cents gerundet werden darf:
Java:
int betrag = 2199;  // 21,99€
int brutto = (int) Math.round(betrag * 1.19);  // 26,17€
Centbeträge werden dadurch zumindest immer genau gespeichert.
 
Um auch nochmal Senf dazuzugeben, falls dir die Kommastellen nach den Centstellen (also Bruchteile von Cents) egal sind, würde ich darauf zurückgreifen, Geldbeträge in Cent als int zu speichern. Wie gesagt, funktioniert nur, wenn Bruchteile von Cents in einer Operation auf Cents gerundet werden darf:
Alternativ zehntel oder Hundertstel Cent zum Rechnen nutzen, je nachdem wie viele Nachkommastellen man bracht. @internet fand Rechnen mit Cents allerdings doof 🙂
 
Alternativ zehntel oder Hundertstel Cent zum Rechnen nutzen, je nachdem wie viele Nachkommastellen man bracht. @internet fand Rechnen mit Cents allerdings doof 🙂
Man könnte diese Funktionalitiäten auch in einer eigenen Klasse verpacken, aber dann sind wir wieder fast am Anfang und bei der Antwort auf eine Frage des TE aus dem anderen Thema:
Es gibt bereits extra Typen für Geld, zB mit JavaMoney
Was genau meinst du mit nachbasteln?
 

Neue Themen


Zurück
Oben