BigDecimal ergänzt unnötige 0 am Ende

Robertop

Bekanntes Mitglied
Hallo zusammen,

mir ist Heute bei der Ausgabe von BigDecimals etwas merkwürdiges aufgefallen:

Java:
System.out.println(BigDecimal.valueOf(0.01));
System.out.println(BigDecimal.valueOf(0.001));
System.out.println(BigDecimal.valueOf(0.0001));
System.out.println(BigDecimal.valueOf(0.00001));

// Ausgabe
// 0.01
// 0.001
// 0.00010
// 0.000010

Anscheinend wird ab dem Wert 0.0001 eine zusätzliche Null am Ende eingebaut. Vom Zahlenwert her macht das zwar keinen Unterschied, ich frage mich aber trotzdem ein wenig, wieso das passiert und ob das eventuell bei Rechnung mit BigDecimal zu einem Problem führt, wenn man einen String in ein BigDecimal umwandelt, bei dem eine solche 0 nicht da ist.
 

LimDul

Top Contributor
Das Problem, was du an der Stelle hast - die Erzeugung des BigDecimal is bereits fragwürdig :) Du gibst da keine exakten Werte rein, sondern double Werte, die bereits mit Ungenauigkeiten behaftet sind.

Ein BigDecimal besteht intern aus - grob gesprochen - zwei Attributen: Value & Scale. Und da der Wert 0.00001 als Double nicht exakt darstellbar ist, wird bei der Erzeugung des BigDecimal bei der Ermittlung des Scale ein um eins größer Scale ermittelt, als man im ersten Moment denkt.
https://docs.oracle.com/en/java/jav...ase/java/math/BigDecimal.html#valueOf(double) Hier wird beschrieben, wie das funktioniert und auf double.toString() verwiesen: https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Double.html#toString(double)

Und da findet sich folgendes
How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.

Ein nicht ganz unkomplexer Algorithmus :)

Sinnvoller ist, wenn man kann, die BigDecimals aus String Werten direkt zu erzeugen, dann hast du diese Probleme nicht.

Bezüglich der Rechnung hast du kein Problem - rechnerisch ist die 0 am Ende im Prinzip egal, der BigDecimal hat halt aktuell einen höheren Scale - aber sobald Rechnungen mit BigDecimal machst, wo das relevant wird, musst du in der Regel eh den Ziel-Scale und das Rundungsverfahren angegeben.
 

Barista

Top Contributor
ich frage mich aber trotzdem ein wenig, wieso das passiert und ob das eventuell bei Rechnung mit BigDecimal zu einem Problem führt, wenn man einen String in ein BigDecimal umwandelt, bei dem eine solche 0 nicht da ist.
Ich habe mal in den Code von BigDecimal geschaut und die Ursache nicht gefunden.

Beim Vergleichen von BigDecimal-Werten muss man auf den Unterschied von equals und compareTo achten, immer compareTo verwenden.

Anhand der Verwendung von compareTo könntest Du testen, ob BigDecimal-Zahlen, die einmal mit und einmal ohne angehängte 0 geparst wurden, in Deinen Anwendungsfällen Probleme machen.
 
K

kneitzel

Gast
Um das Problem evtl. etwas zu veranschaulichen: Du gibst das double Literal 0.0001 aber das ist nicht der Wert, der da dann gespeichert wird.

Für float gibt es da eine sehr interessante Webseite, die dieses Problem sehr gut aufzeigt (float ist wie double nur eben hat es eine kleinere Genauigkeit):

Da kannst Du ja mal angeben: You entered: 0.000001
Dann bekommst Du angezeigt, dass ein float Wert in Wirklichkeit den Wert haben wird: 9.999999974752427078783512115478515625E-7
Also 0.000009999999974752427078783512115478515625

Das Verhalten ist bei Double nicht anders. Nur eben sind da die Abweichungen etwas kleiner.


Oder über die Umwandlung Dezimal -> Dual kann man das auch sehen
Das die Umwandlung nicht immer klappt, kannst Du ja auch erkennen, wenn Du Dir die Werte im Dualen System anschaust:
10 = 2^1 = 2
1 = 2^0 = 1
0.1 = 2^-1 = 0,5
0.01 = 2^-2 = 0,25
0.001 = 2^-3 = 0,125
u.s.w.
Wenn Du also die Dezimal 0.1 in Dualer Schreibweise darstellen müsstest, dann gehst Du diese Folge immer weiter und schaust, ob der Wert >= dem Rest ist:
0.1 ist nicht >= 0,5 also 0.0
0.1 ist nicht >= 0,25 also 0.00
0.1 ist nicht >= 0,125 also 0.000
0.1 ist >= 0.0625 also 0.0001 und Rest ist nun 0.1 - 0.0625 = 0.0375
0.0375 ist >= 0.03125 also 0.00011 und Rest ist nun 0.0375 - 0.03125 ....

Und irgendwann sind die Bits der Mantisse voll, d.h. es ird bei der Umwandlung abgebrochen. Daher ist der Wert dann eben nicht 0.1 sondern etwas davon abweichend.
 

Robert Zenz

Top Contributor
Und um die Verwirrung jetzt noch zu komplettieren: Es gibt auf BigDecimal die Methode BigDecimal.stripTrailingZeros(), welche den BigDecimal auf "den Wert einpasst". Also die internen Werte so aendert dass diese genau auf den abgebildeten Wert passen.

Anschauungsbeispiel:

Java:
System.out.println(BigDecimal.valueOf(0.01)); // 0.01
System.out.println(BigDecimal.valueOf(0.001)); // 0.001
System.out.println(BigDecimal.valueOf(0.0001)); // 0.00010
System.out.println(BigDecimal.valueOf(0.00001)); // 0.000010

System.out.println(BigDecimal.valueOf(0.01).stripTrailingZeros()); // 0.01
System.out.println(BigDecimal.valueOf(0.001).stripTrailingZeros()); // 0.001
System.out.println(BigDecimal.valueOf(0.0001).stripTrailingZeros()); // 0.0001
System.out.println(BigDecimal.valueOf(0.00001).stripTrailingZeros()); // 0.00001

BigDecimal.valueOf(0.00001).equals(new BigDecimal("0.00001")); // false
BigDecimal.valueOf(0.00001).stripTrailingZeros().equals(new BigDecimal("0.00001")); // true

// Gleiches gilt natuerlich auch fuer anders erzeugte BigDecimals:
new BigDecimal("0.100").equals(new BigDecimal("0.1")); // false
new BigDecimal("0.100").stripTrailingZeros().equals(new BigDecimal("0.1")); // true

Wie bereits erwaehnt, "equals" vergleicht ob es sich *wirklich* um zwei idente BigDecimals handelt, also ob auch die internen Werte/Skalierungen entsprechen. "compareTo" vergleicht den dargestellten Wert des BigDecimal.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
M Probleme mit BigDecimal Allgemeine Java-Themen 1
ARadauer Checksumme über BigDecimal Werte Allgemeine Java-Themen 11
M float, double, BigDecimal Allgemeine Java-Themen 5
A Ungünstige BigDecimal-Klasse Allgemeine Java-Themen 44
ARadauer BigDecimal und Not a Number Allgemeine Java-Themen 4
R BigDecimal, Position des Kommas Allgemeine Java-Themen 2
J Umstellung von double auf BigDecimal Allgemeine Java-Themen 5
F BigDecimal devide Allgemeine Java-Themen 5
S wiedermal ein kleines Problem mit BigDecimal Allgemeine Java-Themen 5
B BigDecimal Schleifen Allgemeine Java-Themen 9
J BigInteger und BigDecimal im Eclipse-Debugger Allgemeine Java-Themen 3
hdi Probleme beim Rechnen mit BigDecimal Allgemeine Java-Themen 5
N BigDecimal falsch formatiert bei Locale.GERMANY Allgemeine Java-Themen 3
S String to BigDecimal Allgemeine Java-Themen 6
N BigDecimal.divide Problem Allgemeine Java-Themen 6
N setscale bigdecimal Allgemeine Java-Themen 3
M String to BigDecimal Allgemeine Java-Themen 7
N BigDecimal formatieren Allgemeine Java-Themen 2
André Uhres BigDecimal in HashSet eingefügt, aber nicht in TreeSet Allgemeine Java-Themen 2
G BigDecimal Rundet falsche Allgemeine Java-Themen 4
T bigdecimal.pow(0.5) Allgemeine Java-Themen 19
0 Keine clone-Methode für BigDecimal und BigInteger? Allgemeine Java-Themen 3
I Unnötige Instanzvariablen? Allgemeine Java-Themen 3

Ähnliche Java Themen

Neue Themen


Oben