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 :

Approximation foireuse


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    7
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 7
    Par défaut Approximation foireuse
    Bonjour à tous, j'ai un petit problème que j'aimerais vous soumettre.

    Je possède une classe Vecteur3 générique qui possède comme attributs 3 "coordonnées" du type générique et comme méthodes les getteurs et setteurs qui vont bien avec.
    Dans une autre classe : Feuille, un des attributs est une instance de cette classe avec comme type float. (les coordonnées de la feuille)
    Enfin dans une classe grille, je possède un attribut limitesmin et un autre limitesmax qui sont tous deux de type vecteur3<double>. Cette classe grille lit pour chaque feuille ses coordonnées et les enregistre, si ils sont inférieurs ou supérieurs aux limites, dans les attributs correspondants.

    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
     
    if ((*it)->aFeuille())
            {
                // Variables temporaires pour les tests de limites
                float coordX ;
                float coordY ;
                float coordZ ;
                coordX = (*it)->feuille->coord.getX() ;
                coordY = (*it)->feuille->coord.getY() ;
                coordZ = (*it)->feuille->coord.getZ() ;
     
                // Tests des coordonnées limites
                if (coordX < limitesmin.getX())
                    limitesmin.setX(coordX) ;
                else if (coordX > limitesmax.getX())
                    limitesmax.setX(coordX) ;
                if (coordY < limitesmin.getY())
                    limitesmin.setY(coordY) ;
                else if (coordY > limitesmax.getY())
                    limitesmax.setY(coordY) ;
                if (coordZ < limitesmin.getZ())
                    limitesmin.setZ(coordZ) ;
                else if (coordZ > limitesmax.getZ())
                    limitesmax.setZ(coordZ) ;
    }
    Jusque là tout fonctionne : en sortie de boucle j'ai mes valeurs min et max qui sont ok (1 pour X, 2 pour Y et 3 pour Z, min et max sont égaux poucet exemple mais c'est sans importance)
    C'est la que tout se corse.
    Je souhaite retrancher 0.1 aux valeurs min et ajouter 0.1 aux valeurs max. Le problème est que la valeur obtenue après cette opération n'est pas exacte. Voici le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    tmp1 = limitesmin.getX() - 0.1 ;
    tmp2 = limitesmin.getY() - 0.1 ;
    tmp3 = limitesmin.getZ() - 0.1 ;
    tmp4 = limitesmax.getX() + 0.1 ;
    tmp5 = limitesmax.getY() + 0.1 ;
    tmp6 = limitesmax.getZ() + 0.1 ;
    les 6 variables tmp sont de type double. La valeur que j'obtiens pour tmp1 est 0.900000000000000002. Pour tmp2 1.89999999999999999, pour tmp3
    2.899999999999999999, pour tmp4 1.10000000000000001, pour tmp5 2.10000000000000001 et pour tmp6 3.10000000000000001.
    J'aimerais des valeurs exactes car sinon cela fausse mes calculs situés dans le suite du code.

    Merci d'avance pour votre aide

  2. #2
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Par défaut
    Ces valeurs sont correctes vu la précision des doubles. la différence est inférieure à 10^-16, c'est cohérent.
    Si je peux me permettre, si une telle erreur d'arrondi cause des soucis insolubles, le problème n'est pas très bien posé. Je travaille en général avec des précisions bien inférieures et il n'y a que pour les opérations matricielles complexes genre diagonalisation où j'ai besoin de la précision des doubles.

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    7
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 7
    Par défaut
    Et bien mon calcul suivant pose problème car :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #include <math.h>
    ...
    int tmp;
    tmp = ceil((limitesmax.getX() - limitesmin.getX())/attribut->getDimFX()) ;
    en gros pour X je divise la différence entre les limites par l'echelle que je me suis posé (attribut->getDimFX()) et ici j'obtiens 3 au lieu de 2.

    ceil est sencé arrondir à l'entier supérieur, ce qu'il fait très bien en général mais pas ici à cause de l'erreur d'arrondi.

    je devrais avoir ceil((1.1 - 0.9) / 0.1), soit 2 mais j'ai en fait j'obtient 3 et je ne vois pas pour quelle raison.

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    7
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 7
    Par défaut
    Je viens de remarquer aussi que le 0.1 que je désirais stocker dans attribut pour la division est stocké comme 0.10000000000000001. J'ai donc refais à la main le calcul avec les valeurs "fausses" mais stockées et j'obtient
    2.0000000000000006, qui est donc bien arrondi à 3 par ceil. Les erreurs d'arrondi me posent donc bien des problèmes.

  5. #5
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Par défaut
    Mais tu ne t'en sortiras pas si facilement, malheureusement. Si on assigne 3. à un double à la compilation, c'est pas 3. qui sera stocké car il n'est peut-être pas représentable...
    Regarde si tu ne peux pas te sauver avec std::numeric_limits<>::epsilon qui te donne le plus petit <> tel que 1. + espsilon = 1.

  6. #6
    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 : 41
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    Et en prenant autre chose que ceil, genre quelque chose qui coupe à 0.5 (ie. 2.49 = 2 et 2.51 = 3) ?

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Avril 2005
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2005
    Messages : 41
    Par défaut
    Et pourquoi ne pas se servir d'une fonction qui coupperait non pas à 0.5 mais à epsilon. En effet il peut être utile de concidérer un nombre comme 2.3 comme valant 3 (pour des question de non divisabilité d'objets par exemples) mais de vouloir conciderer toutes valeur inférieures à 2 + epsilon comme valant 2. Dans ce cas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    double espilon = ma_precision;
    /* bla bla bla */
    int tmp = ceil ( mon_calcul - epsilon );
    comme ça si mon_calcul renvoit une valeur inférieure à 2 + epsilon on obtient 2, sinon le résultat reste inchangé. Cela dit cette technique n'est pas terrible à mon goût car elle peut être faussée si epsilon est très petit (de l'ordre de l'erreur générée par le code proposé au début)

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    7
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 7
    Par défaut
    Merci, j'ai créé deux fonctions d'arrondi à l'entier en utilisant la technique du epsilon, ce qui me permet de gérer ces "erreurs" d'approximations comme je l'entend.
    Merci beaucoup à tous.
    ++

  9. #9
    Membre chevronné
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    394
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 394
    Par défaut
    l'erreur d'arrondi peut-être bien supérieure à epsilon, puisque les erreurs d'arrondi se combinent au fil du calcul.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [MySQL] Quelques requêtes foireuses
    Par belzeluc dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 01/02/2006, 20h41
  2. [ImageMagick] Détection des couleurs approximative d'une image ?
    Par SkyDev dans le forum Bibliothèques et frameworks
    Réponses: 4
    Dernier message: 18/01/2006, 14h17
  3. Liberation foireuse
    Par lechewal dans le forum C
    Réponses: 3
    Dernier message: 31/12/2005, 18h19
  4. probleme requete recherche approximative
    Par Immo dans le forum Langage SQL
    Réponses: 5
    Dernier message: 04/08/2005, 16h18
  5. [Astuce] Approximation de racines carrées
    Par Smortex dans le forum Assembleur
    Réponses: 16
    Dernier message: 18/05/2004, 06h17

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