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;
        }
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow