Java Language
Java-Gleitkommaoperationen
Suche…
Einführung
Fließkommazahlen sind Zahlen mit gebrochenen Teilen (normalerweise mit Dezimalpunkt ausgedrückt). In Java gibt es zwei Grundtypen für Gleitkommazahlen, die float
(4 Bytes) und double
(8 Bytes) sind. Diese Dokumentationsseite enthält ausführliche Beispieloperationen, die für Gleitkommazahlen in Java ausgeführt werden können.
Vergleich von Gleitkommawerten
Beim Vergleich von Gleitkommawerten ( float
oder double
) mit relationalen Operatoren sollten Sie vorsichtig sein: ==
!=
, <
Und so weiter. Diese Operatoren liefern Ergebnisse entsprechend den binären Darstellungen der Gleitkommawerte. Zum Beispiel:
public class CompareTest {
public static void main(String[] args) {
double oneThird = 1.0 / 3.0;
double one = oneThird * 3;
System.out.println(one == 1.0); // prints "false"
}
}
Die Berechnung oneThird
hat zu einem kleinen Rundungsfehler geführt. Wenn Sie oneThird
mit 3
multiplizieren, erhalten Sie ein Ergebnis, das sich leicht von 1.0
.
Dieses Problem ungenauer Repräsentationen wird deutlicher, wenn wir versuchen, double
und float
in Berechnungen zu mischen. Zum Beispiel:
public class CompareTest2 {
public static void main(String[] args) {
float floatVal = 0.1f;
double doubleVal = 0.1;
double doubleValCopy = floatVal;
System.out.println(floatVal); // 0.1
System.out.println(doubleVal); // 0.1
System.out.println(doubleValCopy); // 0.10000000149011612
System.out.println(floatVal == doubleVal); // false
System.out.println(doubleVal == doubleValCopy); // false
}
}
Die in Java für die float
und double
Typen verwendeten Gleitkommadarstellungen haben eine begrenzte Anzahl von Ziffern. Für den float
Typ beträgt die Genauigkeit 23 Binärstellen oder etwa 8 Dezimalstellen. Für den double
sind es 52 Bits oder etwa 15 Dezimalstellen. Darüber hinaus führen einige Rechenoperationen zu Rundungsfehlern. Wenn ein Programm Fließkommawerte vergleicht, ist es daher üblich, ein akzeptables Delta für den Vergleich zu definieren. Wenn die Differenz zwischen den beiden Zahlen kleiner als das Delta ist, werden sie als gleich betrachtet. Zum Beispiel
if (Math.abs(v1 - v2) < delta)
Delta-Vergleichsbeispiel:
public class DeltaCompareExample {
private static boolean deltaCompare(double v1, double v2, double delta) {
// return true iff the difference between v1 and v2 is less than delta
return Math.abs(v1 - v2) < delta;
}
public static void main(String[] args) {
double[] doubles = {1.0, 1.0001, 1.0000001, 1.000000001, 1.0000000000001};
double[] deltas = {0.01, 0.00001, 0.0000001, 0.0000000001, 0};
// loop through all of deltas initialized above
for (int j = 0; j < deltas.length; j++) {
double delta = deltas[j];
System.out.println("delta: " + delta);
// loop through all of the doubles initialized above
for (int i = 0; i < doubles.length - 1; i++) {
double d1 = doubles[i];
double d2 = doubles[i + 1];
boolean result = deltaCompare(d1, d2, delta);
System.out.println("" + d1 + " == " + d2 + " ? " + result);
}
System.out.println();
}
}
}
Ergebnis:
delta: 0.01
1.0 == 1.0001 ? true
1.0001 == 1.0000001 ? true
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-5
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-7
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-10
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false
delta: 0.0
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false
Auch für den Vergleich von double
und float
Primitivtypen können statische compare
des entsprechenden Boxtyps verwendet werden. Zum Beispiel:
double a = 1.0;
double b = 1.0001;
System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1
Schließlich kann es schwierig sein zu bestimmen, welche Deltas für einen Vergleich am besten geeignet sind. Ein häufig verwendeter Ansatz ist die Auswahl von Delta-Werten, die unserer Intuition zufolge ungefähr richtig sind. Wenn Sie jedoch die Skalierung und (wahre) Genauigkeit der Eingabewerte sowie die durchgeführten Berechnungen kennen, kann es möglich sein, mathematisch fundierte Grenzen für die Genauigkeit der Ergebnisse und damit für die Deltas zu finden. (Es gibt einen formalen Zweig der Mathematik, bekannt als Numerische Analyse, der Computerwissenschaftlern beigebracht wurde, die diese Art von Analyse abdeckten.)
OverFlow und UnderFlow
Float- Datentyp
Der Float-Datentyp ist ein 32-Bit-IEEE 754-Gleitkomma mit einfacher Genauigkeit.
Float
Maximal möglicher Wert ist 3.4028235e+38
Wenn dieser Wert überschritten wird, wird Infinity
erzeugt
float f = 3.4e38f;
float result = f*2;
System.out.println(result); //Infinity
Float
UnderFlow
Der Mindestwert ist 1.4e-45f. Wenn dieser Wert unter 0 liegt, wird 0.0
erzeugt
float f = 1e-45f;
float result = f/1000;
System.out.println(result);
doppelter Datentyp
Der doppelte Datentyp ist ein 64-bit
IEEE 754-Gleitkomma mit doppelter Genauigkeit.
Double
Überlauf
Maximal möglicher Wert ist 1.7976931348623157e+308
, Wenn er diesen Wert überschreitet, wird Infinity
erzeugt
double d = 1e308;
double result=d*2;
System.out.println(result); //Infinity
Double
UnderFlow
Der Mindestwert ist 4.9e-324. Wenn dieser Wert unterschritten wird, wird 0.0
erzeugt
double d = 4.8e-323;
double result = d/1000;
System.out.println(result); //0.0
Formatieren der Gleitkommawerte
Gleitkommazahlen Zahlen können mit Hilfe von String.format
mit 'f'
String.format
als Dezimalzahl formatiert werden
//Two digits in fracttional part are rounded
String format1 = String.format("%.2f", 1.2399);
System.out.println(format1); // "1.24"
// three digits in fractional part are rounded
String format2 = String.format("%.3f", 1.2399);
System.out.println(format2); // "1.240"
//rounded to two digits, filled with zero
String format3 = String.format("%.2f", 1.2);
System.out.println(format3); // returns "1.20"
//rounder to two digits
String format4 = String.format("%.2f", 3.19999);
System.out.println(format4); // "3.20"
Fließkommazahlen können mit DecimalFormat
als Dezimalzahl formatiert werden
// rounded with one digit fractional part
String format = new DecimalFormat("0.#").format(4.3200);
System.out.println(format); // 4.3
// rounded with two digit fractional part
String format = new DecimalFormat("0.##").format(1.2323000);
System.out.println(format); //1.23
// formatting floating numbers to decimal number
double dv = 123456789;
System.out.println(dv); // 1.23456789E8
String format = new DecimalFormat("0").format(dv);
System.out.println(format); //123456789
Strikte Einhaltung der IEEE-Spezifikation
Standardmäßig halten Gleitkommaoperationen für float
und double
nicht strikt die Regeln der IEEE 754-Spezifikation ein. Ein Ausdruck darf implementierungsspezifische Erweiterungen für den Bereich dieser Werte verwenden. im Wesentlichen so dass sie genauer als erforderlich sein.
strictfp
deaktiviert dieses Verhalten. Es wird auf eine Klasse, Schnittstelle oder eine Methode, und gilt für alles , was darin enthalten ist , wie beispielsweise Klassen, Schnittstellen, Methoden, Konstruktoren Variableninitialisierungen usw. Mit strictfp
, die Zwischenwerte eines Gleitkommaausdruck sein muss innerhalb der Floatwertsatz oder der Doppelwertsatz. Dies führt dazu, dass die Ergebnisse solcher Ausdrücke genau die sind, die die IEEE 754-Spezifikation vorhersagt.
Alle konstanten Ausdrücke sind implizit streng, auch wenn sie sich nicht innerhalb eines strictfp
Bereichs befinden.
strictfp
hat daher den Effekt, dass bestimmte strictfp
manchmal weniger genau sind. Außerdem können Gleitkommaoperationen langsamer werden (da die CPU jetzt mehr Arbeit leistet, um sicherzustellen, dass die native Genauigkeit zusätzlich das Ergebnis nicht beeinflusst). Es führt jedoch auch dazu, dass die Ergebnisse auf allen Plattformen exakt gleich sind. Es ist daher nützlich für Dinge wie wissenschaftliche Programme, bei denen die Reproduzierbarkeit wichtiger ist als die Geschwindigkeit.
public class StrictFP { // No strictfp -> default lenient
public strictfp float strict(float input) {
return input * input / 3.4f; // Strictly adheres to the spec.
// May be less accurate and may be slower.
}
public float lenient(float input) {
return input * input / 3.4f; // Can sometimes be more accurate and faster,
// but results may not be reproducable.
}
public static final strictfp class Ops { // strictfp affects all enclosed entities
private StrictOps() {}
public static div(double dividend, double divisor) { // implicitly strictfp
return dividend / divisor;
}
}
}