Hendoul schrieb:
Aber wie ist das denn beim BigDecimal anders? Hät dieser einfach eine noch viel höhere Präzision oder speichert der die Zahlen irgendwie anders intern?
Kurze Antwort: Ja und Ja
Lange Antwort:
BigDecimal rechnet intern im
Dezimalsystem, daher wird intern die berühmte Zahl "0.1" auch
exakt als 0.1 dargestellt. Siehe auch "A BigDecimal consists of an arbitrary precision integer
unscaled value and a 32-bit integer
scale." (Zitat von
https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html)
Die meisten anderen Implementierungen benutzen
IEEE 754 welches systembedingt einige kleine Schwächen hat, eben daß es z.B. die Zahl "0.1" intern
nicht exakt darstellen kann, da es intern im
Binärsystem (mit endlicher Genauigkeit) arbeitet.
Die (dezimale) Zahl "0.1" in binär ungerechnet lautet:
0.00011001100110011001100110011001100110011001100110011.....
oder exakt
0.0 {0011}Periode (Ich hoffe man kann erkennen was gemeint ist)
Das ist genauso wie im dezimalen Fall mit 1/3= 0.333333333333... bzw. 0.3 Periode.
Aus dieses Umwandlung von dezimal nach binär (und evtl. auch bei der Umwandlung zurück von binär nach dezimal) ergeben sich
Rundungsfehler (insbesondere bei periodischen Zahlen), die evtl. zu Problemen führen.
Wie die Zahl "0.1" im IEEE754-Format aussieht kannst Du Dir
hier ansehen.
Auf der Seite
https://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/ gibt es eine sehr schöne Erklärung des Phänomens in englisch.
Mehrere Ausprägungen des Problems sind auch schön auf
https://www.exploringbinary.com/floating-point-questions-are-endless-on-stackoverflow-com/ dargestellt, exemplarisch sei von meiner Seite das Beispiel
Code:
// from https://www.exploringbinary.com/floating-point-questions-are-endless-on-stackoverflow-com/
public class DecimalTest1
{
public static void main(String args[])
{
double check= 0.615 * 255 -0.515 * 255 -0.100 * 255;
System.out.println(check);
}
}
herausgegriffen, das als Ergebnis
-2.8421709430404007E-14
liefert, obwohl eigentlich "0.0" herauskommen sollte.
Ich habe mir noch als kleine Spielerei ein Programm gebastelt, welches den
exakten Zahlenwert, der sich bei der Umwandlung von dezimal "0.1" in "IEEE754 0.1" ergibt berechnet:
Code:
// see also https://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/
import java.math.BigInteger;
import java.math.BigDecimal;
public class ZeroPointOne
{
public static void main(String args[])
{
// 0.0001100110011001100110011001100110011001100110011001101
BigInteger zp1= new BigInteger("0001100110011001100110011001100110011001100110011001101", 2);
System.out.println("zp1 [10]= " + zp1.toString() );
System.out.println("zp1 [16]= 0x" + zp1.toString(16) );
BigDecimal zeroPointOne= new BigDecimal(zp1).divide(new BigDecimal(2).pow(55));
System.out.println("zeroPointOne= " + zeroPointOne.toString() );
}
}
Das Ergebnis lautet (und zwar
exakt, keine Näherung!):
0.1000000000000000055511151231257827021181583404541015625
RalphS schrieb:
Probleme macht die Division, also braucht man für Genauigkeit eine Fraction Klasse, die eine gebrochene Zahl als Paar (Zähler, Nenner) implementiert und Operationen dafür bereitstellt.
MÖÖÖÖÖÖÖP! Falsch!
Die Division kann zwar evtl. Probleme bereiten, das trifft aber in diesem Fall
nicht zu!
RalphS schrieb:
BigDecimal hat aber eine flexible Genauigkeit bis 32 Stellen.
MÖÖÖÖÖÖÖP! KOMPLETT FALSCH!
Siehe weiter oben mein Zitat von
https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html:
"A BigDecimal consists of an arbitrary precision integer
unscaled value and a 32-bit integer
scale."
BigDecimal kann
problemlos mit
1 Million Stellen rechnen und zwar
exakt!
BigDecimal kann sogar mit "beliebiger" ("arbitrary precision") Genauigkeit rechnen, in der Praxis wird diese durch den Arbeitsspeicher begrenzt!
RalphS schrieb:
Sprich, so Dinge wie 2/5 können mit BigDecimal exakter dargestellt werden.
Richtig exakt geht es aber auch nicht.
MÖÖÖÖÖÖÖP! Schon wieder falsch!
2/5 ist 0,4 und diese Zahl wird intern in BigDecimal als "4*10 hoch -1"
exakt dargestellt!
BeBur schrieb:
Nicht grundsätzlich, das ganze hängt auch an der Antwort auf die Frage "Warum gibt es überhaupt so einen ungenauen Floating-Point Datentyp?"
Antwort:
1. Sehr oft reichen gerundete Ergebnisse
2. floats können sehr schnell auf der CPU (oder einer speziellen Unit) berechnet werden
Richtig!
Und ich würde noch als Drittens ergänzen:
3. Gemessen an der kompakten internen Darstellung (4 bzw. 8 Bytes) liefert die Umsetzung (nahezu) das bestmögliche Ergebnis!
HTH
BigNum