Recherche…


Introduction

Les nombres à virgule flottante sont des nombres comportant des parties fractionnaires (généralement exprimées par un point décimal). En Java, il existe deux types de primitives pour les nombres à virgule flottante: float (utilise 4 octets) et double (utilise 8 octets). Cette page de documentation permet de détailler avec des exemples des opérations pouvant être effectuées sur des points flottants en Java.

Comparer des valeurs en virgule flottante

Vous devez faire attention lorsque vous comparez des valeurs à virgule flottante ( float ou double ) en utilisant des opérateurs relationnels: == != , < Et ainsi de suite. Ces opérateurs donnent des résultats en fonction des représentations binaires des valeurs à virgule flottante. Par exemple:

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

Le calcul oneThird a introduit une erreur d’arrondi minuscule, et lorsque nous multiplions un oneThird par 3 nous obtenons un résultat légèrement différent de 1.0 .

Ce problème des représentations inexactes est plus flagrant lorsque nous essayons de mélanger double et float dans les calculs. Par exemple:

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

Les représentations en virgule flottante utilisées en Java pour les types float et double ont un nombre limité de chiffres de précision. Pour le type float , la précision est de 23 chiffres binaires ou d'environ 8 chiffres décimaux. Pour le type double , il s'agit de 52 bits ou d'environ 15 chiffres décimaux. De plus, certaines opérations arithmétiques introduiront des erreurs d'arrondi. Par conséquent, lorsqu'un programme compare des valeurs à virgule flottante, il est pratique courante de définir un delta acceptable pour la comparaison. Si la différence entre les deux nombres est inférieure au delta, ils sont considérés égaux. Par exemple

if (Math.abs(v1 - v2) < delta)

Delta comparer exemple:

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

Résultat:

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

Pour la comparaison des types primitifs double et float on peut également utiliser une méthode de compare statique du type boxe correspondant. Par exemple:

double a = 1.0;
double b = 1.0001;

System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1

Enfin, il peut être difficile de déterminer quels sont les deltas les plus appropriés pour une comparaison. Une approche couramment utilisée consiste à choisir des valeurs delta qui, selon notre intuition, sont à peu près correctes. Cependant, si vous connaissez l'échelle et la précision des valeurs d'entrée, ainsi que les calculs effectués, il est possible de définir mathématiquement des limites solides sur la précision des résultats et, par conséquent, sur les deltas. (Il existe une branche formelle des mathématiques connue sous le nom d’analyse numérique qui était enseignée à des scientifiques spécialisés dans ce type d’analyse.)

OverFlow et UnderFlow

Type de données flottant

Le type de données float est un virgule flottante IEEE 754 32 bits simple précision.

Float

La valeur maximale possible est 3.4028235e+38 , quand elle dépasse cette valeur, elle produit Infinity

float f = 3.4e38f;
float result = f*2;        
System.out.println(result); //Infinity

Float UNDERFLOW

La valeur minimale est 1.4e-45f, quand est en dessous de cette valeur, elle produit 0.0

    float f = 1e-45f;
    float result = f/1000;
    System.out.println(result);

type de données double

Le type de données double est un virgule flottante IEEE 754 64-bit double précision.

Double OverFlow

La valeur maximale possible est 1.7976931348623157e+308 , lorsqu'elle dépasse cette valeur, elle produit Infinity

double d = 1e308;
double result=d*2;      
System.out.println(result); //Infinity

Double UnderFlow

La valeur minimale est 4.9e-324, quand elle est inférieure à cette valeur, elle produit 0.0

    double d = 4.8e-323;
    double result = d/1000;
    System.out.println(result); //0.0

Formatage des valeurs à virgule flottante

Les nombres à virgule flottante peuvent être formatés sous la forme d'un nombre décimal à l'aide de String.format avec l' String.format 'f'

    //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"

Les nombres à virgule flottante peuvent être formatés en nombre décimal à l'aide de DecimalFormat

   // 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

Adhésion stricte à la spécification IEEE

Par défaut, les opérations à virgule flottante sur float et double ne respectent pas strictement les règles de la spécification IEEE 754. Une expression est autorisée à utiliser des extensions spécifiques à l'implémentation pour la plage de ces valeurs; leur permettant essentiellement d'être plus précis que nécessaire.

strictfp désactive ce comportement. Il est appliqué à une classe, une interface ou une méthode et s'applique à tout ce qui y est contenu, comme les classes, interfaces, méthodes, constructeurs, initialiseurs de variables, etc. Avec strictfp , les valeurs intermédiaires d'une expression à virgule flottante doivent être comprises entre l'ensemble de valeurs flottantes ou l'ensemble de valeurs doubles. Les résultats de ces expressions sont donc exactement ceux prévus par la spécification IEEE 754.

Toutes les expressions constantes sont implicitement strictes, même si elles ne sont pas strictfp dans une portée strictfp .

Par conséquent, strictfp a pour effet net de parfois faire certains calculs coin cas moins précis, et peut également plus lent opérations en virgule flottante (comme la CPU est en train de faire plus de travail pour assurer une précision supplémentaire native ne modifie pas le résultat). Cependant, les résultats sont également identiques sur toutes les plates-formes. Il est donc utile dans des choses comme les programmes scientifiques, où la reproductibilité est plus importante que la vitesse.

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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow