# Ungünstige BigDecimal-Klasse



## aProgrammer (29. Feb 2012)

Sorry,

dass ihr alle jetzt für meinen Ärger herhalten müßt, aber ich muß das jetzt mal los werden. Überall heißt es immer Java, Java, ja Java. Im Großen und Ganzen ist es ja ok, und wenn man denkt wie seine Anfänge waren auch. Aber leider haben die genialen Entwickler auch so manchen Sch..ß eingebaut. Und es wird nicht darüber geredet, bzw. geschrieben.

So wurde bei den primitiven Datentypen der Typ für finanzgenaue Fließkommazahlen schlicht vergessen. Ok, ist nicht nicht so schlimm. Kann vorkommen. Hätte mir auch passieren können.

Was mir aber nicht passiert wäre ist, dass ich das dann nur in Form der Klasse BigDecimal nachimplementiert hätte. Ich hätte einen echten primitiven Datentyp nachimplementiert. Die Klasse BigDecimal ist ein Krampf. 


```
BigDecimal zahl = new BigDecimal("3.0023").add(new BigDecimal("1.0"));
```

ließt sich doch schon mal kacke.

Und dann was mir gerade wieder passiert ist: Ich möchte zwei Geldbeträge auf Gleichheit prüfen:

geldBetrag1 - geldBetrag2 == 0.01

Das Ganze sah vereinfacht ungefähr so aus:


```
for (double differenz = 0.01; differenz <= 0.01; differenz += 0.01) {
  if (differenzOk(new BigDecimal(differenz))) {
    // tue was
  }
}

private boolean differenzOk(BigDecimal differenz) {
  if (geldBetrag1.subtract(geldBetrag2).compareTo(differenz)) {
    return true;
  }
  else {
    return false;
  }
}
```
Dass hier mal true zurückgeliefert wird, darauf kann ich ewig warten. Ich weiß schon was mein Fehler war. Aber eine For-Schleife mit 


```
for (BigDecimal differenz = new BigDecimal("0.01"); differenz.doubleValue() <= 0.10001; differenz = differenz.add(new BigDecimal("0.01"))) {
```
sieht doch einfach sch..ße aus und ich vergesse immer wieder, dass ich von Grund auf immer BigDecimal verwenden muß, wenn's wirklich genau sein soll.

Ich könnt's ja auch noch verkraften, wenn's in allen Programmiersprachen so wäre, aber ich habe jahrelang Delphi programmiert und dort gibt's den primitiven float-Datentyp "currency". Es ist halt schwer vom Komfort wieder runterzukommen. Umgekehrt wär's leichter.

Wie könnt ihr mir helfen? Meine Frage ist einfach die: Gibt's noch mehr Leute, die so empfinden wie ich? Oder nehmt ihr das einfach "Java"-gegeben hin?


Grüße

Wolfgang

PS: Hier noch ein Link zu einem Blog von Markus Kühle, den er unter javathreads.de am 05.03.2009 veröffentlicht hat.

http://javathreads.de/2009/03/niemals-mit-den-datentypen-float-oder-double-geldbetraege-berechnen/

Er spricht den Umstand zwar an, aber er läßt sich auch nicht über die "Eleganz" der Alternativen aus.


----------



## Gast2 (29. Feb 2012)

Für die meisten Berechnungen ist einfach völlig egal ob da hinterm komma irgendwo mal ne falsche Zahl steht, da interessierts einfach niemanden. Würde man immer mit den exakten Werten rechnen, würde das rechnen länger dauern, deswegen gibt man sich da mit der Näherung zufrieden.
Und ob man da jetzt nen neuen primitiven Datentyp hat, oder die Klasse BigDecimal...


----------



## SlaterB (29. Feb 2012)

oben drauf auf deinem Unglück ist 

```
for (BigDecimal differenz = new BigDecimal("0.01"); 
        differenz.doubleValue() <= 0.10001; 
        differenz.add(new BigDecimal("0.01"))) {
```
sogar immer noch falsch, du meinst wohl

```
for (BigDecimal differenz = new BigDecimal("0.01"); 
        differenz.doubleValue() <= 0.10001; 
        differenz = differenz.add(new BigDecimal("0.01"))) {
```
;(

aber der restliche Code strotzt ja auch von Ungenauigkeiten, sind wohl nur Beispiele

-----

zwei Tröst-Versuche:
- nimm doch long als ganze Cents,
falls du verschiedene Anzahlen von Nachkommastellen brauchst, das kann currency in Delphi sicher auch nicht

- wie oft schreibt man schon Währungs-Code? ich rechne auch mit diesen Zahlen täglich, aber der Code dafür ist doch nur ein geringer Anteil,
egal mit welchen Datentypen, verbietet es sich eigentlich allzu oft komplizierte Dinge wie Differenz-Vergleich neu zu implementieren, da können beliebige Fehler reinkommen,
wenn das Grundsystem einmal steht, kann man die Jahre danach hauptsächlich mit add() leben

naja, schwer so allgemein zu beschreiben


----------



## aProgrammer (29. Feb 2012)

@ SlaterB,

danke für deinen Hinweis auf die Fehler, hab's korrigiert. Sind nur reduzierte Fragmente zum Verstehen.

Klar kann man long nehmen und in Cent denken, aber bei Ausgaben muß man auch immer konvertieren.

@ EikeB,

hast Du dir das Beispiel von Markus Kühle wirklich angschaut?
Meine Erfahrung ist die: Sobald es sich um Geldbeträge handelt und es sich um Rechnungen dreht, finden die Kunden die Abweichung im Cent-Bereich nicht mehr lustig. Dabei geht es nicht um den einen Cent, sondern darum, dass die Rechnung per Computer erstellt wird und die sich sagen, wenn der präzise Computer nicht fähig ist den Betrag centgenau zu berechnen, stimmen denn die Zahlen generell? 

Wenn's in Java den Datentyp currency geben würde, dann sehe mein primitives Beispiel so aus:


```
for (currency differenz = 0.01; differenz <= 0.01; differenz += 0.01) {
  if (differenzOk(differenz)) {
    // tue was
  }
}
 
private boolean differenzOk(currency differenz) {
  if (geldBetrag1 - geldBetrag2 == differenz) {
    return true;
  }
  else {
    return false;
  }
}
```

Jetzt bitte keine Kritik, dass der Vergleich in eine extra Methode ausgelagert ist, in meinem Programm werden dort noch andere Sachen geprüft.


Aber generell danke für eure Antwort. Ich merke euch beide kratzt das nicht.

Gruß

Wolfgang


----------



## aProgrammer (29. Feb 2012)

Sorry,

im Eingangsbeitrag steht noch immer:


```
private boolen differenzOk(BigDecimal differenz) {
```

statt:


```
private boolean differenzOk(BigDecimal differenz) {
```


Aber ich darf jetzt scheinbar nicht mehr ändern.


----------



## Gast2 (29. Feb 2012)

Nein das habe ich mir nicht angeschaut, mir ging es auch eher um alle Anwendungsbereiche in denen man diese genauigkeit NICHT braucht. Das es bei Finanzrechnungen auf jeden Cent ankommt ist mir klar.

Wenn man weiß dass man bei double/float aufpassen muss und im Notfall dann auf BigDecimal umschwenkt ist das für mich vollkommen in Ordnung.


----------



## Beni (29. Feb 2012)

Beiträge kann man nach einer gewissen Zeit nicht mehr ändern, ist so gewollt.

Zu deinem Problem: du möchtest primitive Finanztypen, ich möchte primitive Matrixtypen, Hänschen-Fritz möchte primitive Lollipoptypen... das nimmt einfach keine Ende. Und deshalb ist die Entscheidung nur ein Grundset von (erweiterbaren) Funktionen anzubieten schon richtig.

Auch wenn es hard auszusprechen ist: schonmal daran gedacht Scala zu verwenden? Da könntest du dir selbst einen "primitiven" Currency-Datentypen basteln.


----------



## Landei (29. Feb 2012)

Wegen ähnlicher Unbequemlichkeiten mit BigInteger hatte ich schon mal ein kleines Projekt geschrieben, vielleicht kann es ja als Anregung dienen: birpn - BigInteger RPN evaluation in Java - Google Project Hosting


----------



## ...ButAlive (29. Feb 2012)

Ich kann dir nur zustimmen, BigDecimal ist ein rießen Blödsinn. Ich bin auch der Meinung, dass man so einen Datentyp braucht. Ich arbeite im Finanzsektor, wir berechnen Finanzprodukte wie zum Beispiel Kredite, wenn man da bei der Berechnung von Raten irgendwann mal sagt "ach ob da eine Zahl hinter dem Komma falsch ist interessiert mich nicht" hat man schnell über die Laufzeit ein paar Euro zu viel oder zu wenig. Wenn jetzt ein Endkunde mit seinem Taschenrechner nachrechnet wird es peinlich.


Mal abgesehen davon, dass es total ärgerlich ist, dass man es immer noch nicht für nötig hält  wenigsten "+", "-" , "*" und "/" für BigDecimal zu überladen, gibt es noch so einige Highlights in BigDecimal. Hier mal meine persölichen Top 4:

1.


```
BigDecimal b1 = new BigDecimal("1");
BigDecimal b2 = new BigDecimal("1.0");

if(b1.equals(b2)){
    System.out.println("Mathematisch sollte das so sein");
}else{
    System.out.println("Aber BigDecimal hält da nicht viel von");
}
```

Toll equals berücksichtig die Nachkommastellen, das braucht man ja auch so oft. Richtig ist hier:


```
if(b1.compareTo(b2) == 0)
```

Bei dem ganzen Boilerplate-Code den man Dank fehlender Operatorenüberladung schreiben muss, fällt das jetzt nicht mehr ins Gewicht, aber extrem nervend bei Unit-Tests. AssertEquals schlägt bei sowas fehl. Aber das ist immer noch nicht das was ich gerne mit equals hätte, denn wenn b2 null ist kommt eine NullPointerException. Das ist ja auch eigentlich ok, nur der Code den man braucht um 2 BigDecimals auf betragsmäßige Gleichheit zu prüfen verlängert sich auf:


```
if(b2 != null && b1.compateTo(b2)==0)
```

2.


```
double doubleValue = 0.2;
		
BigDecimal fromDouble1 = BigDecimal.valueOf(doubleValue);
BigDecimal fromDouble2 = new BigDecimal(doubleValue);
		
if(fromDouble1.compareTo(fromDouble2)==0){
    System.out.println("Zwei Wege ein ein Double in ein BigDecimal zu verwandeln, die gleich sind");
}
```

Gleich sind beide natürlich nicht. Der erste Weg ruft folgendes auf:


```
new BigDecimal(Double.toString(val));
```

Der 2. Weg macht irgendein tolles Bit-Geschubse. Naja ist sicher performater. Findbug und Co . schlägt übrigens bei new BigDecimal(0.2) an.

3.

Aber wenn beide schon bei BigDecimal nicht gleich sind, dann sollte doch auch folgendes ungleich sein oder?


```
double doubleValue = 0.4;
		
BigDecimal fromDouble1 = BigDecimal.valueOf(doubleValue);
BigDecimal fromDouble2 = new BigDecimal(doubleValue);

double fromBigDecimal1 = fromDouble1.doubleValue();
double fromBigDecimal2 = fromDouble2.doubleValue();

if(fromBigDecimal1 == fromBigDecimal2){
          System.out.println("Jetzt versteh ich die Welt nicht mehr");
}
```

Beim Weg zurück wird das dann wieder korrigiert? Wo ist denn da die Symmetrie? 

4. Apropos Symmetrie, zum Abschluss noch etwas zum Nachdenken:


```
/**
 * Returns a {@code BigDecimal} whose value is {@code (+this)}, and whose
 * scale is {@code this.scale()}.
 * 
 * <p>This method, which simply returns this {@code BigDecimal}
 * is included for symmetry with the unary minus method {@link
 * #negate()}.
 * 
 * @return {@code this}.
 * @see #negate()
 * @since  1.5
 */
public BigDecimal plus() {
     return this;
}
```

Eine Überlebenswichtige Funktion, gut dass die seit 1.5 gibt ich konnte gar nicht mehr ohne. Aber Moment mal this ist symmetrisch zu this * -1 beim einem wird eine neues BigDecimal-Objekt erzeugt, beim anderen nicht. Ok es wäre auch nicht wirklich nötig und spart Speicher, aber wenn man schon auf Teufel komm raus symmetrisch sein will dann bitte doch auch ganz.


----------



## Antoras (29. Feb 2012)

...ButAlive hat gesagt.:


> Ich kann dir nur zustimmen, BigDecimal ist ein rießen Blödsinn. Ich bin auch der Meinung, dass man so einen Datentyp braucht.


Würde es den geben würden auch spezielle Datentypen für Dates, Timestamps etc. gefordert werden. Alles kann man nun mal nicht in eine Sprache aufnehmen.



...ButAlive hat gesagt.:


> Mal abgesehen davon, dass es total ärgerlich ist, dass man es immer noch nicht für nötig hält wenigsten "+", "-" , "*" und "/" für BigDecimal zu überladen


Man hat bei Java von Anfang an die Entscheidung getroffen keine Operatorüberladung in die Sprache aufzunehmen. Das kann jetzt nicht mehr so ohne weiteres eingebaut werden. Ob das jetzt gut oder schlecht ist muss jeder selbst für sich entscheiden. Wem es nicht passt, dem steht es frei eine andere Sprache zu wählen.



...ButAlive hat gesagt.:


> Toll equals berücksichtig die Nachkommastellen, das braucht man ja auch so oft.


Die von Object geerbten Methoden halte ich für eine der größten Designschwächen von Java. Aber das gehört auch zu den Sachen, die einmal festgelegt wurden und nun nachträglich nicht mehr geändert werden können. Da hilft nur Doku lesen und sich zu merken wo Probleme auftreten können.


----------



## irgendjemand (29. Feb 2012)

ich glaube TO und die befürworter das "BigDecimal" überflüssig sei sollten sich mal das hier reinziehen

Gleitkommazahl ? Wikipedia

dort wird ganz eindeutig beschrieben das es einfach NICHT möglich ist einen primitiven datentyp x-beliebiger länge mit x-beliebiger genauigkeit darzustellen ...

dazu das beispiel aus dem artikel

249/20 sind rein mathematisch genau 12,45 ...
das problem ist jetzt aber das diese 12.45 NICHT 100% als float/double dargestellt werden können ...
also wird die nächst mögliche darstellbare zahl gewählt ... was dann bei rückwandlung z.b. "12,44999999900468785" ergeben könnte

der grund : ein rechner KANN es einfach NICHT ... er ist NICHT in der lage soetwas zu 100% genau auf das binärsystem abzubilden ...
und um genau dieses limit zu umgehen ... und es einem rechner trotzdem zu ermöglichen soetwas korrekt darzustellen braucht man eben genau solche wraper ... die es dem rechner ermöglichen mit seinem begrenzten vorrat an darstellbaren zahlen und damit auch der genauigkeit genau soetwas für ihn "krummes" abzubilden und darzustellen ...


um das ganze mal auf ein etwas anderes gebiet der informatik zu legen

es gibt ja das sog. SVG format ... ausgeschrieben : scaled vector graphics ...
wenn man sich mal die erklärung auf wikipedia dazu durchliest fällt einem der total unfug auf :
es soll mit diesem format ermöglicht werden graphische darstellungen beliebig genau zu "zeichnen" und auch zu "speichern" ...
wenn man jetzt aber das ganz umdreht ... und dieses beliebig genau bild darstellen will fällt auf : der monitor hat nur eine auflösung von X mal Y pixeln ... und damit rechnen auch die grafikkarte ... ergo : das angeblich beliebig genau bild lässt sich nur annährend genau darstellen ...

und ein ähnlicher effekt tritt auf wenn man versucht eine fließkommazahl vom dezimalen ins binäre system zu übersetzen ...


vielleicht solltet ihr euch mal über diesen einfachen satz gedanken machen

"ALLES in der informatik hat eine bestimmte auflösung , gleich ob graphisch , numerisch oder sonst was"

man kann sowohl von den beiden beispielen auf diesen satz schließen ... aber auch beide damit "erklären" ...


----------



## ThreadPool (29. Feb 2012)

aProgrammer hat gesagt.:


> [...]
> 
> Wie könnt ihr mir helfen? Meine Frage ist einfach die: Gibt's noch mehr Leute, die so empfinden wie ich? Oder nehmt ihr das einfach "Java"-gegeben hin?
> [...]



Nein du bist der Einzige. Andere ziehen Runden vor, welches in Finanzapplikationen üblicherweise zwei Nachkommastellen einschließt und gut bis in einen sehr hohen zweistelligen Milliardenbereich funktioniert. Mit Double lassen sich dann Zahlen IMHO vernünftig bis zur 14-15 Nachkommastelle abbilden. Wenn mehr relevant sind sollte man zu Bigdecimal greifen.



...ButAlive hat gesagt.:


> Ich kann dir nur zustimmen, BigDecimal ist ein rießen Blödsinn.[...]



Ich sehe bei dir höchstens Probleme mit der Verwendung von BigDecimal. Die Dokumentation von equals besagt unmissverständlich was zum Vergleich herangezogen wirden. Die anderen Dinge kann man kompensieren indem man die Methode setScale mit Angabe einer Größenordnung und Rundungsmodus aufruft.


----------



## SlaterB (29. Feb 2012)

irgendjemand hat gesagt.:


> dort wird ganz eindeutig beschrieben das es einfach NICHT möglich ist einen primitiven datentyp x-beliebiger länge mit x-beliebiger genauigkeit darzustellen ...


zum einen ist nicht gesagt, dass dieser primitive Datentyp wie alle anderen aufgebaut sein muss,
es könnte das dicke BigDecimal dahinterstehen und trotzdem wie ein primitiver Datentyp funktionieren,
alles von der Sprache verdeckbar,

zum anderen zeigt ja long, wie man doch exakt speichern und rechnen kann,
es könnte intern also long verwendet, dazu Nachkommstellen definiert werden, dann ergeben sich eben x Vorkommastellen

(nicht dass ich dafür wäre, aber denkbar ist es nunmal)



Antoras hat gesagt.:


> Die von Object geerbten Methoden halte ich für eine der größten Designschwächen von Java. Aber das gehört auch zu den Sachen, die einmal festgelegt wurden und nun nachträglich nicht mehr geändert werden können. Da hilft nur Doku lesen und sich zu merken wo Probleme auftreten können.


die Vererbung bestimmt doch nicht, ob BigDecimal.equals() Scale berücksichtigen muss?


----------



## ...ButAlive (29. Feb 2012)

Antoras hat gesagt.:


> Würde es den geben würden auch spezielle Datentypen für Dates, Timestamps etc. gefordert werden. Alles kann man nun mal nicht in eine Sprache aufnehmen.



Ich will keinen speziellen Datentypen haben, ich hätte gerne eine Klasse die genau rechnet und ich damit keinen Code schreiben muss, der Augenkrebs erzeugt. Operatoren-Überladung vom Hersteller und eine equals-Methode, die sich nicht unerwartet verhält würde mir dabei schon helfen.  



Antoras hat gesagt.:


> Man hat bei Java von Anfang an die Entscheidung getroffen keine Operatorüberladung in die Sprache aufzunehmen. Das kann jetzt nicht mehr so ohne weiteres eingebaut werden. Ob das jetzt gut oder schlecht ist muss jeder selbst für sich entscheiden. Wem es nicht passt, dem steht es frei eine andere Sprache zu wählen.



Java ist momentan die am weitest verbreitete Sprache. Hauptsächlich im wirtschaftlichen Bereich, und da geht es nun mal meistens ums Geld. Viele renommierte Autoren wie z.B. Fowler und Bloch sagen, nimm für Geld BigDecimal. Da halte ich die Forderung nach überladenen Operatoren für legitim. 

Es ist ja nicht so, dass ich die Operatoren überladen will, sondern dies schon im Compiler drin ist. So wie man es auch String mit  "+" gemacht hat. Genau so etwas würde ich mir für BigDecimal wünschen. Ich steh auch nicht alleine mit diesem Wunsch da, dass stand auch schon mal für der Änderungsliste für JDK 1.6 und 1.7, soweit ich mich erinnere, aber irgendwie fällt das immer wieder unter den Tisch.



Antoras hat gesagt.:


> Die von Object geerbten Methoden halte ich für eine der größten Designschwächen von Java. Aber das gehört auch zu den Sachen, die einmal festgelegt wurden und nun nachträglich nicht mehr geändert werden können. Da hilft nur Doku lesen und sich zu merken wo Probleme auftreten können.



BigDecimal hat eine eigene equals-Methode, diese vergleicht ob beide den gleichen Wert haben und die gleiche Anzahl an Nachkommastellen. Der Anwendungsfall dafür ist mir irgendwie schleierhaft. Doku lesen kann ich schon, den Code den ich aber schreiben muss um festzustellen ob 2 BigDecimals den gleichen Wert haben finde ich wenig intuitiv und lesbar.

@irgendjemand


irgendjemand hat gesagt.:


> ich glaube TO und die befürworter das "BigDecimal" überflüssig sei sollten sich mal das hier reinziehen
> 
> Gleitkommazahl ? Wikipedia



Es geht nicht darum, dass BigDecimal überflüssig ist, sondern einfach nur blöd zu handhaben, den Sinn hinter BigDecimal hat glaube ich jeder hier verstanden.

@ThreadPool


ThreadPool hat gesagt.:


> Nein du bist der Einzige. Andere ziehen Runden vor, welches in Finanzapplikationen üblicherweise zwei Nachkommastellen einschließt und gut bis in einen sehr hohen zweistelligen Milliardenbereich funktioniert. Mit Double lassen sich dann Zahlen IMHO vernünftig bis zur 14-15 Nachkommastelle abbilden. Wenn mehr relevant sind sollte man zu Bigdecimal greifen.



Wie rundet man denn mit double? Du kannst es nur bei der Ausgabe runden, aber während des Rechnens geht es nicht. Und genau hier liegt das Problem, durch Anfangsungenauigkeiten wird es während des Rechnens immer ungenauer.


----------



## schalentier (29. Feb 2012)

Ich muss auch mit Geldern rechnen auf Arbeit und kann mich dem allgemeinen Tenor nur anschliessen: BigDecimal ist einfach Murks.

Sinnvoll ist glaub ich, fuer das Rechnen mit Geld eine eigne Klasse zu verwenden, die intern mit BugDecimal rechnet. Da kann man dann zumindestens die groebsten Probleme an einer zentralen Stelle verwalten (equals, einheitlicher Scale, Rundungsmode, etc). Zudem koennte man dort auch Waehrungen mit einbauen, um so einfaches Umrechnen zu ermoeglichen. Logisch, der Nervfaktor bei Formeln ohne Operatorenueberladen bleibt natuerlich. Moeglicherweise lohnt sich sogar der Einsatz einer DSL (bzw einer passenden Scriptsprache) fuer die eigentlichen Berechnungen (dazu wuerde man sinnigerweise saemtliche Formeln in einer Klasse/Modul sammeln). 

Fliesskommazahlen sollte man auf gar keinen Fall fuer Rechnungen mit Geld benutzen!


----------



## aProgrammer (29. Feb 2012)

SlaterB hat gesagt.:


> zum einen ist nicht gesagt, dass dieser primitive Datentyp wie alle anderen aufgebaut sein muss,
> es könnte das dicke BigDecimal dahinterstehen und trotzdem wie ein primitiver Datentyp funktionieren,
> alles von der Sprache verdeckbar,





...ButAlive hat gesagt.:


> Java ist momentan die am weitest verbreitete Sprache. Hauptsächlich im wirtschaftlichen Bereich, und da geht es nun mal meistens ums Geld. Viele renommierte Autoren wie z.B. Fowler und Bloch sagen, nimm für Geld BigDecimal. Da halte ich die Forderung nach überladenen Operatoren für legitim.
> 
> Es ist ja nicht so, dass ich die Operatoren überladen will, sondern dies schon im Compiler drin ist. So wie man es auch String mit  "+" gemacht hat. Genau so etwas würde ich mir für BigDecimal wünschen. Ich steh auch nicht alleine mit diesem Wunsch da, dass stand auch schon mal für der Änderungsliste für JDK 1.6 und 1.7, soweit ich mich erinnere, aber irgendwie fällt das immer wieder unter den Tisch.



Das wären für mich zwei gangbare Alternativen.

@ irgendjemand

Natürlich kenne ich die Problematik. Es geht ja nur darum einen hinreichend genauen Datentyp für finanztechnische Operationen zu schaffen. Borland verwendet dazu 8 Bytes. Der Datentyp heißt dort übrigens korrekt *C*urrency. (Schon lange kein Delphi mehr programmiert.) Wenn Borland das kann, warum kann's dann Sun nicht? (Oder besser: Warum konnte es Sun dann nicht.)

Es ist einfach so, die Entwickler haben's beim ersten Design der Sprache einfach verpennt einen solchen Datentyp vorzusehen. Das werfe ich ihnen ja gar nicht vor. Mir passieren solche Dinge auch laufend. Was ich ihnen aber vorwerfe ist, dass sie bei der Reparatur geschlampt haben. 

Wenn sie's als richtigen primitiven Datentyp nachträglich hätten einführen wollen, hätten vermutlich alle bis dahin verteilten VMs nachgebessert werden müssen. Aber SlaterBs Vorschlag mit dem Verstecken hätte auch dieses Problem umgangen. Man hätte die ganze häßliche Umwandlerei vom Compiler machen lassen können und intern mit der versteckten BigDecimal-Klasse arbeiten können. 
Oder eben wie ...ButAlive vorgeschlagen hat für die wichtigsten Operatoren eine Überladung in den Compiler einbauen können, wie er richtig bemerkt, es bei den Strings gemacht wurde. (Und seine berechtigte Kritik an der BigDecimal-Klasse aus seinem ersten Beitrag gleich miteinfliesen lassen.) 



ThreadPool hat gesagt.:


> Nein du bist der Einzige.


Nein ich korrigiere dich. Mit ...ButAlive und schalentier sind wir immerhin zu dritt.


----------



## Antoras (29. Feb 2012)

...ButAlive hat gesagt.:


> Es ist ja nicht so, dass ich die Operatoren überladen will, sondern dies schon im Compiler drin ist. So wie man es auch String mit "+" gemacht hat. Genau so etwas würde ich mir für BigDecimal wünschen.


Da hat man das Problem, dass man keine Grenze mehr ziehen kann zwischen der Abhängigkeit vom Compiler zur Sprache. Wenn der Compiler zu stark mit der Sprache verknüpft wird (wie es bei Java der Fall ist), ist das nur ein Zeichen für fehlenden Ausdrucksstärke seitens der Sprache.

Es muss entschieden werden ob Teile der Sprache, die eigentlich äquivalent zueinander sind, höher gestellt werden als andere (String z.B. kann mehr als andere Objekte -> Es kann ohne new erzeugt werden, Überladung von + usw.). Dieses Verhalten für weitere Klassen zu implementieren bringt nur Unruhen, dann will jeder so was. Bevor man das macht sollte man Operatorüberladung einführen und somit die Sprache mächtiger machen und nicht den Compiler.


----------



## aProgrammer (29. Feb 2012)

schalentier hat gesagt.:


> Ich muss auch mit Geldern rechnen auf Arbeit und kann mich dem allgemeinen Tenor nur anschliessen: BigDecimal ist einfach Murks.



Beim Wort Murks fällt mir der Ausdruck "*Murksaround*" ein. Das war ein geflügeltes Wort in der vorletzten Firma, wenn es um solche Lösungen ging.


----------



## ...ButAlive (29. Feb 2012)

Antoras hat gesagt.:


> Da hat man das Problem, dass man keine Grenze mehr ziehen kann zwischen der Abhängigkeit vom Compiler zur Sprache. Wenn der Compiler zu stark mit der Sprache verknüpft wird (wie es bei Java der Fall ist), ist das nur ein Zeichen für fehlenden Ausdrucksstärke seitens der Sprache.
> 
> Es muss entschieden werden ob Teile der Sprache, die eigentlich äquivalent zueinander sind, höher gestellt werden als andere (String z.B. kann mehr als andere Objekte -> Es kann ohne new erzeugt werden, Überladung von + usw.). Dieses Verhalten für weitere Klassen zu implementieren bringt nur Unruhen, dann will jeder so was. Bevor man das macht sollte man Operatorüberladung einführen und somit die Sprache mächtiger machen und nicht den Compiler.



Da magst du ja recht haben, die Sprache ist nicht mächtig genug an manchen Stellen. Generelle Operatorenüberladung würde ich aber nicht fordern.

Das man String nicht mit new erzeugen muss ist ok denn die alternative wäre grausam:


```
new String(new char[]{'H','a','l','l','o'});
```

Aber das "+" von String fand ich noch nie konsequent, denn so schlimm wäre ausschreiben ja auch nicht.


```
String a = "Hallo";
StringBuilder builder = new StringBuilder(a);
builder.append(a);
a = builder.toString();
```

Klar freue ich mich das es das gibt, aber ich würde ja verstehen wenn es ohne wäre, weil man konsequent sein wollte.

In den letzten Versionen ist doch eh immer mehr in den Compiler geflossen


```
Integer i = 123;
```

ist eigentlich:


```
Integer i = Interger.valueOf(123);
```


```
List<String> strings = ...;
for(String s:String){}
```

ist eigentlich:


```
List<String> strings = ...;
Iterator<String> it = stings.iterator();
while(it.hasNext()){}
```


```
String value = "Hallo";
switch(value){
    case "Hallo": break;
    case "Welt": break;
}
```

ist eigentlich


```
String value = "Hallo";
int tmp = value.hashCode();
switch(tmp)
{
   case "Hallo".hashCode():break;
   case "Welt".hashCode():break;
}
```

Es gibt noch so ein paar Sachen die in den letzten Jahren in den Compiler geflossen sind. Viele Sachen davon sind in der Kategorie "nice to have" aber um mit Geld sauber rechnen zu können muss man immer noch unlesbaren Code schreiben.


----------



## Cola_Colin (29. Feb 2012)

Niemand hindert euch daran, eine eigene Klasse zu schreiben, die eure spezifischen Probleme gut löst und sauber lesbaren Code ermöglicht. Nur über das doch so unzulängliche BigDecimal zu meckern hilft wenig.


----------



## Antoras (29. Feb 2012)

Cola_Colin hat gesagt.:


> Niemand hindert euch daran, eine eigene Klasse zu schreiben, die eure spezifischen Probleme gut löst und sauber lesbaren Code ermöglicht. Nur über das doch so unzulängliche BigDecimal zu meckern hilft wenig.


Von gut kann keine Rede sein. Manche Probleme können damit gelöst werden - sinnvolle Bezeichner für die Operationen gehören aber nicht dazu.


----------



## Cola_Colin (29. Feb 2012)

Das mag stimmen, ist aber auch der einzige wirklich problematische Punkt. Mich hat es nie gestört hat, eben .add(...) zu verwenden statt +, aber ich habe auch nicht täglich mit entsprechendem Code zu tun. Nachdem ich mich aber erst vor kurzem mit C Code herumgeärgert habe, in dem einige Operatoren wirklich nervig überladen waren, bin ich sehr froh darüber, dass das in Java so nicht passieren kann.


----------



## ThreadPool (29. Feb 2012)

...ButAlive hat gesagt.:


> [...]
> @ThreadPool
> 
> Wie rundet man denn mit double? Du kannst es nur bei der Ausgabe runden, aber während des Rechnens geht es nicht. Und genau hier liegt das Problem, durch Anfangsungenauigkeiten wird es während des Rechnens immer ungenauer.



Wie? Mit ein wenig Fantasie z.B. wie unten, wobei das totaler "Overkill" wäre da man hier z.B. nur das Endergebnis runden müsste. Rundungsfehler werden versucht so gut es geht zu kontrollieren,  darüber haben sich schon eine Menge anderer Leute den Kopf zerbrochen und der Welt diverse Infos zur Verfügung gestellt z.B. Berechnung von Korrekturfaktoren in Schleifen usw. Man ist eben gezwungen darüber nachzudenken wie viel Genauigkeit benötigt wird  und was zu erwarten ist.

Um noch mal die Currency von Delphi aufzugreifen, das ist z.B. eine Festkommazahl mit NUR 4 Nachkommastellen, dafür genügt es ein BigDecimal mit setScale(4) und dem gewünschten Rundungsmodus erstellen. Normalerweise kapselt man sowas sowieso in einem unveränderlichen Money-Typen und hat dann "seine" saubere Schnittstelle.

[Java]
public class Test {

	public static double bankersRounding(double d) {
	    return Math.round(d * 100) / 100.0;
	}

	public static void main(String[] args) {
		double res = bankersRounding(bankersRounding(0.1+0.2)-0.3);
		System.out.println(res);

		double sum = 0.0;
		for(int i = 0; i < 10; i++){
			sum = bankersRounding(sum+0.1);
		}

		System.out.println(sum);
	}
}
[/Java]


----------



## SlaterB (29. Feb 2012)

...ButAlive hat gesagt.:


> ```
> String value = "Hallo";
> switch(value){
> case "Hallo": break;
> ...


das ist nur deine Interpretation oder?
Hashcode ist nicht eindeutig..


----------



## ...ButAlive (1. Mrz 2012)

SlaterB hat gesagt.:


> das ist nur deine Interpretation oder?
> Hashcode ist nicht eindeutig..



Ne das wird schon so gemacht, die genaue Spezifikation finde ich jetzt gerade nicht, aber hier mal ein Blog-Eintrag auf der Oracle-Seite: https://blogs.oracle.com/darcy/entry/project_coin_string_switch_break


----------



## SlaterB (1. Mrz 2012)

na immerhin steht dort dass auf equals aufgepasst wird

```
switch(s.hashCode()) { // cause NPE if s is null
	          case 3010735: // "azvl" and "bmjrabc".hashCode()
                      if (s.equals("azvl"))
		        break $azvl;          
                      else if (s.equals("bmjrabc"))
                        break $bmjrabc;
                      else
                        break $default_label;
```


----------



## ...ButAlive (1. Mrz 2012)

Stimmt, ich frag mich gerade, wieso sie dass so machen, wenn man sowieso für jede String eine Prüfung auf equals braucht, wozu ist dann das switch noch gut? Aber ich will jetzt nicht zu weit vom eigentlichen Thema abschweifen.


----------



## Empire@Work (1. Mrz 2012)

Wer schwatzt hier eigentlich immer sowa intelligents wie in delphi ging das auch...
der Punkt ist BigDecimal ist aus guten grund NICHT!! currency genannt worden,
weil es eben auch ermöglich mit wissenschaftlichen werten zu arbeiten, zb zwei 1 gigabytegroße Zahlen zu addieren, da es vollkommen von der lowlevel hardwareplatform abstrahiert.
Und jetzt sowas wie eine 8byte zahl zu forden ist ein schritt zurück. Operatorüberladung wünsche ich mir allerdings auch öfters, zb für Vektoren,Quaternions,BigDecimal.


----------



## langhaar! (1. Mrz 2012)

aProgrammer hat gesagt.:


> Es ist einfach so, die Entwickler haben's beim ersten Design der Sprache einfach verpennt einen solchen Datentyp vorzusehen.



Die Aussage halte ich für arrogant und anmaßend.
Bis zum Nachweis des Gegenteils gehe ich davon aus, dass bewusst kein primitiver Datentyp für Geld aufgenommen wurde und nicht, weil irgendwer etwas 'verpennt' hat.


----------



## SlaterB (1. Mrz 2012)

dass die Entscheidung bewußt gefallen und nachträglich ständig diskutiert wird, steht außer Frage
("Ich steh auch nicht alleine mit diesem Wunsch da, dass stand auch schon mal für der Änderungsliste für JDK 1.6 und 1.7, soweit ich mich erinnere, aber irgendwie fällt das immer wieder unter den Tisch.")

'verpennen' soll sicher eine Bewertung dieser Entscheidung(en) sein


----------



## tfa (1. Mrz 2012)

> bewusst kein primitiver Datentyp für Geld aufgenommen wurde und nicht, weil irgendwer etwas 'verpennt' hat.


Und das ist gut so. 
Java ist eine allgemein verwendbare Programmiersprache und keine Spezialsprache für Businessanwendungen! Wo kommen wir hin, wenn jede Fachdomäne ihre eigenen primitiven Datentypen bekommen soll? Eine unnötig aufgeblasene, komplizierte Sprache. 
Für einen "currency-Typ" kann man genauso gut ein long nehmen und Cents oder Millicents abspeichern.

currency : Java Glossary


----------



## ThreadPool (1. Mrz 2012)

tfa hat gesagt.:


> Und das ist gut so.
> Java ist eine allgemein verwendbare Programmiersprache und keine Spezialsprache für Businessanwendungen! Wo kommen wir hin, wenn jede Fachdomäne ihre eigenen primitiven Datentypen bekommen soll? Eine unnötig aufgeblasene, komplizierte Sprache.
> [...]



Es gibt auch allgemein verwendbare Programmiersprachen die etwas Ähnliches unterstützen. Nehmen wir z.B. Ada, die bieten von vornherein floating point und fixed point an. Darüberhinaus sind diese "konfigurierbar" d.h. es ist festlegbar wie viele Bits für die Zahl verwendet werden sollen oder die Basis usw. dabei wird dann noch garantiert in welchen Grenzen es sicher ist mit den Zahlen zu arbeiten.  
Wird sowas wie Money benötigt sagt man z.B. type Money is delta 0.1 digits 14; um sich einen Dezimaltypen zu basteln der wie "gewohnt" funktioniert und nicht wirklich langsamer ist.


----------



## tfa (1. Mrz 2012)

> Es gibt auch allgemein verwendbare Programmiersprachen die etwas Ähnliches unterstützen. Nehmen wir z.B. Ada, die bieten von vornherein floating point und fixed point an. Darüberhinaus sind diese "konfigurierbar" d.h. es ist festlegbar wie viele Bits für die Zahl verwendet werden sollen oder die Basis usw. dabei wird dann noch garantiert in welchen Grenzen es sicher ist mit den Zahlen zu arbeiten.


Das macht es aber zu einem sehr flexiblen, allgemein verwendbaren Datentypen. Dagegen spricht ja nichts.


----------



## aProgrammer (1. Mrz 2012)

@ all:

Ich als Themenersteller bin zum einen der, der immer von Delphi und Currency redet. In diesem Zusammenhang korrigiere ich mich auch gleich. In einer Antwort an 



> @ irgendjemand
> 
> Natürlich kenne ich die Problematik. Es geht ja nur darum einen hinreichend genauen Datentyp für finanztechnische Operationen zu schaffen. Borland verwendet dazu 8 Bytes. Der Datentyp heißt dort übrigens korrekt Currency. (Schon lange kein Delphi mehr programmiert.) Wenn Borland das kann, warum kann's dann Sun nicht? (Oder besser: Warum konnte es Sun dann nicht.)



irgendjemand hatte auf eine Wikipedia-Artikel verwiesen und wollte daraufhinweisen, dass durch die Digitalisierung nicht jede beliebe Auflösung möglich ist. Ist ja auch korrekt. Aber die Auflösung reicht locker aus um einen Datentyp zu schaffen der hinreichend genau ist um bei finanztechnischen Operationen die Rundungungsfehler so klein zu halten, dass sie keine (negative) Rolle spielen. 

Und schrieb ich eben jenen Satz:



> Wenn Borland das kann, warum kann's dann Sun nicht? (Oder besser: Warum konnte es Sun dann nicht.)



Das ist natürlich völliger Quatsch. Dass Sun es konnte, bewiesen sie mit der Klasse BigDecimal. Diese Klasse ist sogar weit allgemeiner gehalten, wie hier zu Recht angemerkt wird, als, dass man nur währungstechnisch damit arbeiten kann.

Der Punkt ist aber der, dass der Quellcode bei Verwendung der Klasse BigDecimal sehr schlecht lesbar wird. Zumindest für mich, der ich gewohnt bin beim Rechnen eben die gewohnten Operatoren wie + - / *  % == > >= < <= etc. zu verwenden. 

Meiner Meinung nach fehlen bei der Klasse BigDecimal auch die Vergleichsoperatoren >  < >= <=. Wenn ich mit doubleValue() konvertieren muß, dann laufe ich wieder in die Ungenauigkeiten rein, die ich mit BigDecimal vermeiden möchte.

Als ich den Thread erstellt habe war ich einfach über den Umstand gefrustet, dass mir das Handling der Klasse BigDecimal nicht gefällt und ich wollte lediglich wissen, ob andere das auch so empfinden.

Darum ziehe ich jetzt mal ein kleines Fazit, um die Diskussion vielleicht auch zu beenden: Ja es scheint in der Tat noch 4 oder 5 Leute hier im Forum zugeben, die die Arbeit mit BigDecimal nicht optimal finden. Das tröstet mich etwas und läßt mich hoffen, dass (jetzt) Oracle vielleicht doch mal ein Einsehen hat und für BigDecimal die Rechen- und Vergleichsoperatoren überlädt.

Könnt ihr damit Leben und wir beenden die Diskussion?

Gruß

Wolfgang


----------



## Gast2 (1. Mrz 2012)

> Meiner Meinung nach fehlen bei der Klasse BigDecimal auch die Vergleichsoperatoren > < >= <=. Wenn ich mit doubleValue() konvertieren muß, dann laufe ich wieder in die Ungenauigkeiten rein, die ich mit BigDecimal vermeiden möchte.


Musst du ja nicht, dafür implementiert BigDecimal das Comparable Interface und du kannst compareTo nutzen (welche auch von sortierten Listen, Sets oder Bäumen genutzt wird).
Java ist ne objektorientierte Sprache, und da ist es an der Tagesordnung Methoden aufzurufen, ich find das nicht schlimm


----------



## fastjack (1. Mrz 2012)

```
BigDecimal zahl = new BigDecimal("3.0023").add(new BigDecimal("1.0"));
```

was spricht eigentlich dagegen (außer dem Namen  ):


```
BigDecimal zahl = MyNiceBigDecimalUtil.add("3.0023", "1.0");
```

and so on...


----------



## ...ButAlive (1. Mrz 2012)

Ich hab heute nochmal ein paar Informationen zu dem Thema gesucht, die ich euch nicht vorenthalten möchte. Aber zuerst einmal noch ein kleines Beispiel wieso ich gerne Operatorenüberladung für BigDecimal hätte. 

Eine Methode die aus einem Nettopreis einen Bruttopreis berechnet. So sieht es aus wenn man double nimmt.


```
public double bruttoPreis(double nettoPreis, double steuerSatz) {
   return nettoPreis + nettoPreis * (steuerSatz / 100);
}
```

Und nun das gleiche mit BigDecimal:


```
public BigDecimal buttoPreis(BigDecimal nettoPreis, BigDecimal steuerSatz) {
   return nettoPreis.add(nettoPreis.multiply(steuerSatz.divide(BigDecimal.valueOf(100))));
}
```

Klar kann man die Hundert als Konstante raus ziehen und die Zwischenergebnisse in Variablen merken, aber ich finde die Double-Variante sieht schöner aus. Ich habe täglich mit komplexen Formeln zu tun, würde ich hin und wieder mal zwei BigDecimals addieren müssen würde ich mich nicht beschweren.

Trotzdem brauche aber keinen primitiven Datentypen, mir würde ja Compilerunterstützung reichen. Mal davon abgesehen, könnte man sich kaum mehr leisten, das als primitiven Datentypen zu machen, denn in der JVM wäre aktuell gerade noch für 48 ByteCode-Instructions frei. Das klingt viel, aber alleine für int gibt es um die 38 Instruktionen. Genau so viele bräuchte man dafür auch plus nochmal 4  für Casting-Operationen zu BigDecimal. Danach wäre noch 6 Plätze frei. Dass keiner von Sun/Oracle das machen wollte ist mir klar.

Im Netz findet man einen (meiner Meinung nach) bösen Hack um Operatoren-Überladung in Java nach zubauen. A real operation overloading in plain Java


----------



## Antoras (2. Mrz 2012)

...ButAlive hat gesagt.:


> Klar kann man die Hundert als Konstante raus ziehen und die Zwischenergebnisse in Variablen merken, aber ich finde die Double-Variante sieht schöner aus. Ich habe täglich mit komplexen Formeln zu tun, würde ich hin und wieder mal zwei BigDecimals addieren müssen würde ich mich nicht beschweren.


Natürlich sieht die Version mit Operatoren schöner aus, die Frage ist ob man damit leben kann oder nicht. Du kannst es anscheinend nicht. Da wir aber nicht viel mehr machen können als deine Klagen anhören, solltest du dich lieber direkt an Oracle wenden, vllt. kannst du dort etwas erreichen.



...ButAlive hat gesagt.:


> Im Netz findet man einen (meiner Meinung nach) bösen Hack um Operatoren-Überladung in Java nach zubauen.


Wer Typsicherheit gegen Schönheit eintauschen möchte kann das gerne nutzen. Wer das braucht kann aber gleich zu einer Interpretersprache greifen die Operntorüberladung unterstützt und dort den Code schreiben und von Java aus aufrufen. Dann muss man nichts selbstgebautes nutzen bei dem man am Schluss nicht weiß ob es überhaupt funktioniert.


----------



## xehpuk (2. Mrz 2012)

Kurz auf den Trugschluss im erwähnten Blog-Eintrag eingegangen …


> Auf der Rechnung ist schön zu erkennen, dass 38,29 EUR – 38,29 EUR nicht immer 0 ergeben. Jedenfalls dann nicht, wenn man mit Fließkommazahlen rechnet.




```
x - x == 0
```
 gilt immer, auch wenn x eine Fließkommazahl ist. Die angezeigten Beträge waren halt gerundet; die zur Rechnung herbeigezogenen nicht.


----------



## fastjack (2. Mrz 2012)

Operator-Overload-Hack? Also wer es braucht bitte. Verpacke die Rechenoperationen einfach in eine Helperklasse und fertig, wo liegt das Problem?

P.S.: Java ist ja bekanntlich obektorientiert, allerdings nicht rein. In einer reinen objektorientierten Sprache hättest Du gar keine primitives mehr.


----------



## ...ButAlive (3. Mrz 2012)

Klar lagert man so viel Funktionalität wie möglich so aus, dass man sie wiederverwenden kann. Das mach ich jetzt nicht so wie du vorgeschlagen hast, sondern versuche fachlich nach Formeln zu gruppieren, somit kann man schon damit leben. Der Code mit den Formeln ist halt aber nicht schön zu lesen. Ob man es nun braucht oder nicht, dazu gibt es unterschiedliche Meinungen und hat mit Geschmack und wie sehr man darunter "leidet" zu tun.

Aber wieso meinst du, dass vom Compiler "überladene" Operatoren OOP widersprechen? 

In Scala kann man 
	
	
	
	





```
val c = a.add(b)
```
 auch 
	
	
	
	





```
val c = a add b
```
. Da ist Scala fast alles als Methodenname genommen werden darf, kommt man dann auf 
	
	
	
	





```
val c = a + b
```
. Für mich ist das nur Syntax.


----------



## fastjack (3. Mrz 2012)

> Aber wieso meinst du, dass vom Compiler "überladene" Operatoren OOP widersprechen?


Habe ich nicht behauptet. Ich habe nur geschrieben, daß es in einer reinen objektorientierten Sprache keine primitives gibt, weil eben alles Objekte sind.


----------



## ...ButAlive (3. Mrz 2012)

fastjack hat gesagt.:


> Habe ich nicht behauptet. Ich habe nur geschrieben, daß es in einer reinen objektorientierten Sprache keine primitives gibt, weil eben alles Objekte sind.



Ok dann haben wir an einander vorbei geredet, denn ich will auch nicht, dass BigDecimal ein primitiver Datentyp ist, sondern sich nur so benutzen lässt wie einer.


----------



## SlaterB (3. Mrz 2012)

fastjack hat gesagt.:


> P.S.: Java ist ja bekanntlich obektorientiert, allerdings nicht rein. In einer reinen objektorientierten Sprache hättest Du gar keine primitives mehr.





man muss aber dazu sagen 'allerdings nicht rein' insofern, dass es ohne die primitiven schlimm aussähe im Moment,
ohne ein einziges int, kann man noch so viele Klassen definieren, nichts passiert 

also um rein zu werden müsste irgendeine andere Zahl-Form erst noch hineinkommen


----------



## Landei (3. Mrz 2012)

Kann man z.B. mit Autoboxing lösen. In Scala gibt es nur einen Typ [c]Int[/c], der sich wie eine Klasse verhält, aber an den entsprechenden Stellen eben zu einem primitiven int ungeboxt wird, ohne dass man es mitbekommt. Natürlich ist das nur der VM-Kompatibilität geschuldet, so hatte Smalltalk von Anfang an nur Objekte.


----------

