Java double ungenau?
Ich versuche in Java mit großen Zahlen zu rechnen. Jedes mal wenn ich aber mit der double über 2*10^16 komme, rechnet java anscheinend nicht mehr richtig. Wenn ich z.B +1 rechnen will, ändert sich die Zahl nicht mehr. Wenn ich +8, +9, +10 oder +11 rechne, rechnet er nur 12 dazu. Außerdem ist jede Zahl nur noch gerade.
3 Antworten
Gleitkommaoperationen werden nur mit einer endlichen Genauigkeit, der so genannten Maschinengenauigkeit, durchgeführt.
Bei double precision Gleitkommazahlen nach IEEE 754 (der heutzutage von quasi allen Prozessoren umgesetzt wird) beträgt sie 2^(-53) = ~1.1 * 10^(-16).
Das ist der relative Fehler, der bei jeder einzelnen Gleitkommaoperation passiert.
Wenn Deine Zahl selbst bereits größer, als 10^16 ist, ist der absolute Fehler bereits größer, als eins.
Wenn Du höhere Präzision benötigst, gibt es in Java die Datentypen java.math.BigDecimal (für Gleitkommazahlen), sowie java.math.BigInteger (für Ganzzahlen). Diese sind aber etwas umständlicher zu verwenden und Operationen sind natürlich viel langsamer, als mit den elementaren Datentypen, da sie in mehreren Schritten in Software berechnet werden, während die nativen Datentypen bestenfalls in exakt einer Maschinenoperation verarbeitet werden können.
Richtig, double ist ungenau, da der Rechner die Zahlen nur anders speichern kann (binäre Darstellung eines Bruchs und eines Exponenten).
Wenn du genaue Werte brauchst, musst du BigDecimal nehmen.
Hier mehr: https://stackoverflow.com/questions/322749/retain-precision-with-double-in-java
Moment, ich schreibe mal einen BigDecimal-Code zu deinem Beispiel oben.
import java.math.BigDecimal;
public class Test {
public static void main(String[] args) {
BigDecimal prothNumber = new BigDecimal("21181");
BigDecimal one = new BigDecimal("1");
BigDecimal two = new BigDecimal("2");
BigDecimal eight = new BigDecimal("8");
BigDecimal forty = new BigDecimal("40");
BigDecimal solution = prothNumber.multiply(two.pow(forty.intValue()).add(one));
BigDecimal solutionplus1 = solution.add(one);
BigDecimal solutionplus8 = solution.add(eight);
System.out.println(solution.toString());
System.out.println(solutionplus1.toString());
System.out.println(solutionplus8.toString());
}
}
Wow, ok das sieht ein bisschen kompliziert aus, aber danke dafür, sehr hilfreich.
Ich dachte double wird erst bei kleinen Kommazahlen ungenau, aber nicht im normalen Zahlenbereich von 0 bis 2^63.
Das hängt mit der Zahlendarstellung zusammen. Double hat 15.9 signifikante Stellen. Da 10^16 bereits 17 Stellen aufweist, werden kleine Zahlen "verschluckt". Es stehen einfach nicht genügend bits zur Verfügung, alle Stellen genau abzuspeichern.
Wie genau implementier ich die BigDecimal? Ich komm da nicht drauf.