IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Précision de calcul


Sujet :

Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé Avatar de Mucho
    Inscrit en
    Décembre 2005
    Messages
    221
    Détails du profil
    Informations forums :
    Inscription : Décembre 2005
    Messages : 221
    Par défaut Précision de calcul
    Une petite question sur la précision de calcul,

    j'ai trouvé pas mal de messages sur le forum qui parlent de ce problème mais pas de réponse que je comprenne vraiment.
    Voilà un exemple tout simple que je ne comprend pas :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    double a = 230.13075;
        double b = 178.17765;
     
        double result = a - b;
    Dans ce cas result vaut 51.953100000000006
    Je ne sais pas trop comment corriger le problème ... pour un calcul aussi simple !

    Et avec des float c'est pire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    float a = 230.13075f;
        float b = 178.17765f;
     
        float result = a - b;
    Result vaut 51.95311 au lieu de 51.9531

    Est-ce que quelqu'un à une méthode claire pour avoir un calcul exact ?

  2. #2
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Par défaut
    C'est un problème de représentation interne propre au système, pas au langage.
    Tu as essayé avec java.math.BigDecimal ? Il me semble que ça résout le problème (pas le temps de vérifier...)
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
    Que la force de la puissance soit avec le courage de ta sagesse.

  3. #3
    Membre confirmé Avatar de Mucho
    Inscrit en
    Décembre 2005
    Messages
    221
    Détails du profil
    Informations forums :
    Inscription : Décembre 2005
    Messages : 221
    Par défaut
    Merci mais ...
    J'avais aussi essayé BigDecimal et ça ne semble pas résoudre le problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    BigDecimal a = new BigDecimal(230.13075);
    BigDecimal b = new BigDecimal(178.17765);
     
    double result = a.subtract(b, MathContext.UNLIMITED).doubleValue();
    result vaut alors 51.953100000000006

  4. #4
    Membre éprouvé
    Avatar de David Gimelle
    Profil pro
    Développeur Java
    Inscrit en
    Janvier 2007
    Messages
    79
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Janvier 2007
    Messages : 79
    Par défaut Precision
    Si tu veux garder la precision des BigDecimal, reste en bigdecimal et ne les transforme pas en double. Vue que les doubles ne te garantissent pas la precision que tu souhaites:

    Exemple :
    MathContext mContext=new MathContext(10);

    BigDecimal bdA= new BigDecimal(a,mContext);
    BigDecimal bdB=new BigDecimal(b,mContext);
    BigDecimal res=bdA.subtract(bdB,mContext);

    System.out.println("res :"+res);

    David Gimelle
    Developpeur J2EE
    http://getj2ee.over-blog.com

  5. #5
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 967
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 967
    Par défaut
    Gio,

    En repassant en double, tu recrées le problème : le nombre de valeurs exactement représentables avec des flottants à taille fixe est très petit par rapport à la dynamique possible (c'est à dire par rapport aux valeurs acceptées par les variables de ce type).
    Il est donc rarissime de tomber sur une de ces valeurs.

  6. #6
    Membre confirmé Avatar de Mucho
    Inscrit en
    Décembre 2005
    Messages
    221
    Détails du profil
    Informations forums :
    Inscription : Décembre 2005
    Messages : 221
    Par défaut
    Un test plus complet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    BigDecimal a = new BigDecimal(230.13075);
    BigDecimal b = new BigDecimal(178.17765);
    double test2 = 51.9531;
    BigDecimal test3 = new BigDecimal(51.9531);
     
    double result1 = a.subtract(b, MathContext.UNLIMITED).doubleValue();
    double result2 = test2;
    double result3 = test3.doubleValue();
    result1 = 51.953100000000006
    result2 = 51.9531 // donc un double peut sans problème prendre cette valeur
    result3 = 51.9531 // donc il semble que la conversion en double n'affecte pas la valeur

    En repassant en double, tu recrées le problème
    Je ne comprends pas pourquoi (cf test3 et result3 ci-dessus)

    Si tu veux garder la precision des BigDecimal, reste en bigdecimal et ne les transforme pas en double. Vue que les doubles ne te garantissent pas la precision que tu souhaites
    Mais pourtant les doubles (et même les floats il me semble) peuvent sans problème contenir le résultat (cf test2 et test3 ci-dessus).
    En fait je voudrais juste faire une fonction qui revoie un double avec la bonne valeur (i.e. la variable result2 ou result3 ci-dessus)

  7. #7
    Membre éprouvé
    Avatar de David Gimelle
    Profil pro
    Développeur Java
    Inscrit en
    Janvier 2007
    Messages
    79
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Janvier 2007
    Messages : 79
    Par défaut
    Ce bout de code devrais resoudre ton probleme :

    // Fixe le scale a 10 digit
    MathContext mContext=new MathContext(10);

    // calcule avec 10 digit
    BigDecimal bdA= new BigDecimal(a,mContext);
    BigDecimal bdB=new BigDecimal(b,mContext);
    BigDecimal res=bdA.subtract(bdB,mContext);
    System.out.println("res :"+res);

    // une fois le calcule termine tu peux le transformer en double
    double d = res.doubleValue();
    System.out.println("d:"+d);

  8. #8
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,


    Comme cela a été dit, les float/double ont une imprécision plus ou moins grande qui peut fausser les résultats. Or comme tu utilises des doubles pour initialiser tes BigDecimal tu conserves cette "imprécision" !

    La preuve :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    BigDecimal a = new BigDecimal(230.13075);
    BigDecimal b = new BigDecimal(178.17765);
     
    System.out.println(a);
    System.out.println(b);
    Le code ci dessus t'affiche ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    230.130750000000006139089236967265605926513671875
    178.177649999999999863575794734060764312744140625
    Il s'agit de l'approximation représentant les doubles 230.13075 et 178.17765, et lorsque tu fait la soustraction des deux tu obtiens 51.953100000000006275513442233204841613769531250...



    Donc tu as deux solutions :
    • Soit tu as besoin de calcul super-précis, et tu utilises la classe BigDecimal mais en aucun cas des floats ou des doubles qu'il te faut bannir !
    • Soit tu peux de contenter d'une précision moindre, ce qui est le cas dans la plupart des applications, et tu formates les float/double afin de n'afficher qu'une nombre limité de décimales afin de te préserver des erreurs d'approximations.



    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    	double a = 230.13075;
    	double b = 178.17765;
    	double c = a - b;
     
    	// Affichage par défaut (avec les approximations) :
    	System.out.println(a);
    	System.out.println(b);
    	System.out.println(c);
     
    	System.out.println();
     
    	// On utilise un formatter avec un maximum de 8 décimales :
    	NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
    	nf.setMaximumFractionDigits(8);
     
    	// Affichage OK (sans les approximations) :
    	System.out.println( nf.format(a) );
    	System.out.println( nf.format(b) );
    	System.out.println( nf.format(c) );
    Ce qui donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    230.13075
    178.17765
    51.953100000000006
     
    230.13075
    178.17765
    51.9531
    a++

Discussions similaires

  1. Précision de calcul
    Par elglantosimpatico dans le forum MATLAB
    Réponses: 7
    Dernier message: 19/04/2012, 11h21
  2. scipy.poly1d : précision du calcul des racines
    Par ryced dans le forum Calcul scientifique
    Réponses: 5
    Dernier message: 25/01/2010, 10h04
  3. précision de calcul de fsolve
    Par Nabuchodonosor15 dans le forum MATLAB
    Réponses: 3
    Dernier message: 22/07/2009, 13h34
  4. Choisir la précision pour calculs en nombres flottants
    Par ciol2.6.12 dans le forum Algorithmes et structures de données
    Réponses: 2
    Dernier message: 02/06/2008, 14h14
  5. Précision de calculs trigo.
    Par Clad3 dans le forum C++
    Réponses: 11
    Dernier message: 23/10/2007, 14h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo