Double, Float et BigDecimal

Gestion des nombres à virgule en Java

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com

En Java les nombres à virgule peuvent être représentés soit par double, soit par float. Cependant, les deux se comportent différement et les calculs sur un ne donneront pas forcément les mêmes résultats que sur l'autre.

La différence principale entre ces deux types primitifs repose sur l'exactitude des résultats. Float est codé sur 32 bits. Tandis que double est codé sur 64 bits. Il est conseillé d'utiliser floats pour économiser de la mémoire si la précision des résultats importe peu. Dans le cas contraire il faut privilégier double, malgré la taille deux fois plus grande. En ce qui concerne la rapidité d'exécution, il n'y a pas de différences sur les machines récentes.

public class FloatDoubleDifference {
    public static void main(String[] args) {
        System.out.println("Min value of float = " + Float.MIN_VALUE);
        System.out.println("Max value of float = " + Float.MAX_VALUE);
        System.out.println("Max length of float = " + Float.SIZE);
        System.out.println("Min value of double = " + Double.MIN_VALUE);
        System.out.println("Max value of double = " + Double.MAX_VALUE);
        System.out.println("Max length of double = " + Double.SIZE);

        float exFloat = 3.2f;
        double exDouble = 3.2;	

        if (exFloat == 3.2) {
            System.out.println("exFloat equals to 3.2");
        } else {
            System.out.println("exFloat doesn't equal to 3.2");
        }

        if (exDouble == 3.2) {
            System.out.println("exDouble equals to 3.2");
        } else {
            System.out.println("exDouble doesn't equal to 3.2");
            System.out.println("exDouble's value is " + exDouble);
        }

        System.out.printf("Float value : %.20f\n", 3.2f);
        System.out.printf("Double value : %.20f\n", 3.2);
    }
}

Cet exemple montre quelle est la différence de précision pour le chiffre 3,2. S'il s'agit d'un double, le résultat est correct (sur l'écran on voit "Double value : 3,20000000000000000000"). En ce qui concerne float, le résultat du test est faux (l'écran affiche "Float value : 3,20000004768371600000"). Cela démontre bien que pour rester précis, il vaut mieux se tourner vers double.

Cependant, aucun des deux types ne devrait pas être utilisé tout seul pour les opérations financières. Float et double peuvent falsifier les résultats. Regardons ceci sur un exemple d'achat :

public class BigDecimalCase {
    public static void main(String[] args) {
        System.out.println("Double result for 1.13 - 0.22 = " + (1.13 - 0.22));
        System.out.println("Float result for 1.13f - 0.22f = " + (1.13f - 0.22f));
    }
}

Le cas de figure va afficher les messages suivants :

Double result for 1.13 - 0.22 = 0.9099999999999999
Float result for 1.13f - 0.22f = 0.90999997

Le résultat attendu est pourtant 0.91. Pour l'achever, il faut effectuer les calculs sur BigDecimal.

import java.math.BigDecimal;

public class BigDecimalCase {
    public static void main(String[] args) {
        float toPay = 1.13f;
        float toReturn = 0.22f;
        double toPayD = 1.13;
        double toReturnD = 0.22;

        BigDecimal toPayBd = new BigDecimal(Float.toString(toPay));
        BigDecimal toReturnBd = new BigDecimal(Float.toString(toReturn));
        System.out.println("BigDecimal result for converted floats 1.13 - 0.22 = " + toPayBd.subtract(toReturnBd));
 
        BigDecimal toPayDBd = new BigDecimal(Double.toString(toPayD));
        BigDecimal toReturnDBd = new BigDecimal(Double.toString(toReturnD));
        System.out.println("BigDecimal result for converted double 1.13 - 0.22 = " + toPayDBd.subtract(toReturnDBd));   
    }
}

Le résultat retourné est finalement correct :

BigDecimal result for converted floats 1.13 - 0.22 = 0.91
BigDecimal result for converted double 1.13 - 0.22 = 0.91

L'avantage d'utiliser BigDecimal est une possibilité de spécifier la précision du résultat (le nombre de chiffres qui apparaîtront après le virgule). C'est la méthode setScale() qui s'en charge. Elle prend comme paramètre l'instance de la classe MathContext qui détermine la précision du résultat (nombre de chiffres à afficher).

On peut également coupler les opérations effectuées sur BigDecimal avec les types primitifs double et float. Avec l'utilisation des classes utilitaires d'internationalisation, on peut facilement gérer l'affichage des chiffres en fonction du pays d'origine.

Voici l'exemple complet, contenant les arrondis et les calculs corrects des prix :

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Locale;

public class BigDecimalCase {
    public static void main(String[] args) {
        float toPay = 1.13f;
        float toReturn = 0.22f;
        double toPayD = 1.13;
        double toReturnD = 0.22;

        System.out.println("Double result for 1.13 - 0.22 = " + (toPayD - toReturnD));
        System.out.println("Float result for 1.13f - 0.22f = " + (toPay - toReturn));

        BigDecimal toPayBd = new BigDecimal(Float.toString(toPay));
        BigDecimal toReturnBd = new BigDecimal(Float.toString(toReturn));
        System.out.println("BigDecimal result for converted floats 1.13 - 0.22 = " + toPayBd.subtract(toReturnBd));
 
        BigDecimal toPayDBd = new BigDecimal(Double.toString(toPayD));
        BigDecimal toReturnDBd = new BigDecimal(Double.toString(toReturnD));
        System.out.println("BigDecimal result for converted double 1.13 - 0.22 = " + toPayDBd.subtract(toReturnDBd));
    
        MathContext mc = new MathContext(5);
        BigDecimal bdScale = new BigDecimal("1.53929");    
        bdScale.setScale(3, BigDecimal.ROUND_HALF_UP);
        System.out.println("BigDecimal with 2 numbers scale " + bdScale.round(mc));

        System.out.println("Show big amount in the American and French format");
        BigDecimal bigTest = new BigDecimal("759403.535");
        NumberFormat usFormat = NumberFormat.getCurrencyInstance(Locale.US); 
        double btDoubled = bigTest.doubleValue();
        String btString = usFormat.format(btDoubled);
        System.out.println("American representation of 759403.535 " + btString);
        Locale frLocale = new Locale("fr", "FR", "EURO");
        NumberFormat frFormat = NumberFormat.getCurrencyInstance(frLocale); 
        String btStringFr = frFormat.format(btDoubled);
        System.out.println("French representation of 759403.535 " + btStringFr);
    }
}

L'écran affichera :

Double result for 1.13 - 0.22 = 0.9099999999999999
Float result for 1.13f - 0.22f = 0.90999997
BigDecimal result for converted floats 1.13 - 0.22 = 0.91
BigDecimal result for converted double 1.13 - 0.22 = 0.91
BigDecimal with 2 numbers scale 1.5393
Show big amount in the American and French format
American representation of 759403.535 $759,403.54
French representation of 759403.535 759 403,54 €
http://stackoverflow.com/questions/6232446/float-or-double Comment on peut comparer un double et un float ? BigDecimal !
Bartosz KONIECZNY Classes

Une question ? Une remarque ?

*

*

Un conseil JavaScript

Comment récupérer la hauteur de l'élément avec jQuery ?

On peut le faire avec deux méthodes. La première fonctionne correctement pour tous les navigateurs sauf Internet Explorer. Pour le cas des navigateurs Microsoft, elle marche uniquement quand la hauteur est spécifiée en dur :

$('#element').css('height')
L'alternative est la méthode height() qui est gérée par tous les navigateurs, même si l'élément a la hauteur "auto" :
$('#element').height();