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

C++ Discussion :

Imprécision des float et map


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    216
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2003
    Messages : 216
    Points : 74
    Points
    74
    Par défaut Imprécision des float et map
    Bonjour,

    Si je crée une map de cette façon : std::map<float, int>, comment la comparaison entre les clefs va elle se faire ?

    Un simple == entre deux float ?

    Merci d'avance...

  2. #2
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    std::map n'utilise pas l'égalité, mais une relation d'ordre. Cela utilisera toujours et uniquement l'opérateur <.

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    216
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2003
    Messages : 216
    Points : 74
    Points
    74
    Par défaut
    Merci pour ta réponse.

    Quand on utilise la fonction insert de std::map, je comprend parfaitement qu'il n'a pas besoin d'utiliser "==".

    Mais quand on va rechercher un élément comme ceci: maMap[2.5], il doit bien utiliser le "==" ? autrement je voit pas comment il ferait !

  4. #4
    Membre habitué Avatar de Ksempac
    Inscrit en
    Février 2007
    Messages
    165
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 165
    Points : 185
    Points
    185
    Par défaut
    C'est du a une propriété mathematique toute bete, mais a laquelle on ne pense pas immédiatement : si 2 nombres sont égaux, A < B et B < A renvoient tous les deux FALSE.

    Exemple :

    A = 2, B = 3.
    A < B renvoie TRUE
    B < A renvoie FALSE

    A = 3, B = 2
    A < B renvoie FALSE
    B < A renvoie TRUE

    A = 2, B = 2
    A < B renvoie FALSE
    B < A renvoie FALSE

    C'est une chose qu'il ne faut pas oublier de prendre en compte lorsqu'on surcharge l'opérateur <

    A part ca, meme pour un insert on a besoin de reconnaitre les cas d'egalité, puisque la map doit s'assurer de ne pas avoir de doublon

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    216
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2003
    Messages : 216
    Points : 74
    Points
    74
    Par défaut
    Ouais je vois mais donc un map<float, int> ça n'a fonctionnera pas bien dans ce cas à cause de l'imprecision des float alors :

    float a=1.0f;
    float b=2.1f-1.1f;
    maMap[a] = 5;
    maMap[b] = 8;

    Il risque de m'ajouter 2 données différentes dans la map alors que "a" et "b" sont cencé être de la même valeur. C'est bien ça ?

  6. #6
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 064
    Points : 1 053
    Points
    1 053
    Par défaut
    L'imprécision dans les calculs sur flottant n'apparait pas au petit bonheur la chance, dans ton calcul a et b contiennent exactement la même chose.
    Cela n'a de risque d'arriver que si tu fais des division. Par exemple il se peut que dans les formules 2.0/3.0 et (1.0/3.0)*2.0 les tout derniers bits utilisés pour représenter la valeur 0.666... soient différents.
    Dans l'absolu, il est vrai que si il y a une différence de ne serais-ce qu'un millionième entre deux flottants map créera deux entrées différentes.
    Si cela te pose un problème, il est possible de personnaliser le foncteur utilisé par map pour faire les comparaisons pour lui imposer une précision limitée (à 1,2,3 chiffres après la virgule par exemple).

  7. #7
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    Peut être y a-t-il un problème de conception derrière ?

    De toute manière, si tu dois faire quelque chose de vraiment précis, cela sera vrai sur toute ton appli et il va falloir utiliser une autre représentation pour les flottants.

    Si tu peux te permettre des imprecisions dans les calculs ailleurs dans l'appli, alors tu peux ici aussi et il te faudra considérer égales des valeurs à x.xx près.

    Tu dois pouvoir changer le prédicat qui fait la comparaison dans la map, comme j'ai eu à le faire récement pour un std::set.

  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Citation Envoyé par Ksempac Voir le message
    C'est une chose qu'il ne faut pas oublier de prendre en compte lorsqu'on surcharge l'opérateur <

    A part ca, meme pour un insert on a besoin de reconnaitre les cas d'egalité, puisque la map doit s'assurer de ne pas avoir de doublon
    Etant donné que la std::map utilise seulement l'opérateur plus petit, elle considère que si
    a n'est pas plus petit que b et que
    b n'est pas plus petit que a alors
    a est égal à b...

    Il faut noter que, dans certains cas, cela risque d'apporter quelques bugs originaux (même si je n'ai aucune idée de ce qu'ils peuvent être )

    Comme quoi, il y a parfaitement moyen de ne travailler qu'avec l'opérateur "plus petit"

    Evidemment, il reste le fait que les réels (floats ou doubles d'ailleurs) souffrent d'une imprécision qui est, principalement due à la représentation binaire.

    Comme toute comparaison entre des réels, il faut donc penser à prendre en compte le "décalage" en dessous duquel un nombre reste identique, c'est à dire s'il se trouve entre les X - std::limits<float>::epsilon() et X + std::limits<float>::epsilon() (où X est le nombre utilisé )

    L'un dans l'autre, ca pourrait très bien servir de "théorie" pour créer le foncteur ad-hoc
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    216
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2003
    Messages : 216
    Points : 74
    Points
    74
    Par défaut
    Merci pour vos réponses.

    zais_ethael j'aurais logiquement le même raisonnement que toi à propos des float mais :

    1) Dans la FAQ, j'ai vu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    float f1 = 0.1f;
    float f2 = 1.1f;
    float f3 = f2 - f1;
     
    // Version incorrecte ne tenant pas compte des imprécisions
    if (f3 == 1.0f)
    {
        // Pratiquement jamais vrai !
    }
    http://cpp.developpez.com/faq/cpp/?p...ions_flottants

    2) Ce programme m'affiche "2" à l'execution :
    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
    #include <iostream>
    #include <map>
    using namespace std;
     
    int main()
    {
    	std::map<float, int> maMap;
     
    	float a=1.0f;
    	float b=2.1f-1.1f;
    	maMap[a] = 5;
    	maMap[b] = 8;
     
    	cout<<maMap.size()<<endl;
     
    	return 0;
    }
    Alors que si je remplace la ligne "float b=2.1f-1.1f;" par "float b=1.0f;",le programme m'affiche "1" !!!

  10. #10
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    Tu n'as pas répondu si travailler avec des imprecisions pour la map serait une option ?

    Si oui, dans l'esprit, la solution serait ça :

    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #include <iostream>
    #include <map>
    using namespace std;
     
     
    struct PredicatLessFloat
    {
      static float const imprecision;
     
      bool operator () ( float const & f1, float const & f2 ) const
      {
        float f3 = f2 - f1 - imprecision;
        return f3 > 0;
      }
    };
     
    float const PredicatLessFloat::imprecision = 0.000001;
     
     
    int main()
    {
      std::map<float, int, PredicatLessFloat> maMap;
     
      float a=1.0f;
      float b=2.1f-1.1f;
      maMap[a] = 5;
      maMap[b] = 8;
     
      // affiche 1
      cout<<maMap.size()<<endl;
     
      return 0;
    }

  11. #11
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Un tel prédicat ne correspond pas à ce qui est requis pour une map. Il faut en effet que ce soit un "strict weak ordering", c'est à dire (avec a==b défini comme !(a<b) && !(b<a)):

    - !(a<a)
    - a<b && b<c => a<c
    - a==b && b==c => b==c

    Or avec ta proposition de prédicat, le troisière point n'est pas respecté. Imaginons une précision de 0.1 et les valeurs a=0.91, b=1, c=1.09.

    On a bien a==b (car, à cette précision, a n'est ni plus grand ni plus petit que b).
    On a bien b==c (car, à cette précision, b n'est ni plus grand ni plus petit que c).

    Par contre, on a a<c.


    Conclusion : Indexer une map par des flottants n'est probablement pas une bonne idée. Il faudrait voir quel est le problème initial pour faire une contre proposition.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  12. #12
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par zais_ethael Voir le message
    L'imprécision dans les calculs sur flottant n'apparait pas au petit bonheur la chance,
    C'est vrai et c'est important.

    Mais

    dans ton calcul a et b contiennent exactement la même chose.
    est faux.

    Cela n'a de risque d'arriver que si tu fais des division.
    est faux.

    Ca arrive chaque fois que le resultat d'un calcul n'est pas exactement representable. Ca peut arriver avec des additions et des soustractions, et ca arrive souvent quand on convertit d'une representation a une autre -- en particulier de la representation decimale utilisee pour les litteraux a la representation binaire interne.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  13. #13
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 064
    Points : 1 053
    Points
    1 053
    Par défaut
    Ha, je viens d'aller voir quelques sites et il semblerait que mon prof d'assembleur n'avait pas jugé utile de nous dire que les réels en base 10 n'étaient pas forcément représentables en base 2. Autant pour moi.

  14. #14
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 942
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 942
    Points : 5 654
    Points
    5 654
    Par défaut
    Qoe,
    Citation Envoyé par zais_ethael Voir le message
    Ha, je viens d'aller voir quelques sites et il semblerait que mon prof d'assembleur n'avait pas jugé utile de nous dire que les réels en base 10 n'étaient pas forcément représentables en base 2. Autant pour moi.
    Une petite réflexion te l'aurait appris, sinon il faudrait une résolution infinie.
    Si les cons volaient, il ferait nuit à midi.

Discussions similaires

  1. [MDA] Tour d'horizon des outils de mapping?
    Par big x dans le forum MDE
    Réponses: 7
    Dernier message: 03/06/2012, 11h35
  2. [Javascript] Précision des float
    Par NicoNours dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 15/03/2006, 09h12
  3. Récupérer des floats à partir d'une string
    Par hamster dans le forum SL & STL
    Réponses: 8
    Dernier message: 09/06/2005, 08h10
  4. Réponses: 9
    Dernier message: 13/04/2005, 22h08
  5. Test if sur des float
    Par Minuit dans le forum Linux
    Réponses: 2
    Dernier message: 26/03/2005, 13h08

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