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 :

Différence bizarre d'un calcul avec variables intermédiares


Sujet :

C++

  1. #1
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut Différence bizarre d'un calcul avec variables intermédiares
    Bonjour,
    J' ai un phénomène très bizarre que j'explique pas, à vous de juger.

    On a deux objets, Foo et Bar :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Foo
    {
      double getX()
      {
        return (m_X);
      }
      double getY()
      {
        return (m_Y);
      }
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Bar
    {
      double getValue()
      {
        return (m_value);
      }
    };
    Maintenant, ce qu'il y a de bizarre, c'est qu'en faisait cela:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    result = (Foo.getX() - Bar.getValue()/2 + Foo.getY()/2)/Foo.getX();
    // result = 0.17271511157289223814537137968728
    Et cela:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    double x = Foo.getX();
    double value = Bar.getValue();
    double y =  Foo.getY();
    result = (x - value/2 + y/2)/x;
    // result = 0.17271511157289287652361053915229
    On obtient pas le même resultat comme vous pouvez le voir.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    // result = 0.17271511157289223814537137968728
    // result = 0.17271511157289287652361053915229
    Je ne comprends vraiment pas car les getters() ne font que renvoyer la valeurs et rien de plus.
    Si quelqu'un à une explication, je suis preneur.

    Merci de votre aide.

  2. #2
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Est-ce que les variables membre m_x, m_y et m_value sont de type double?
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  3. #3
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    Oui, désolé de ne pas avoir précisé cela.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Foo
    {
      double getX()
      {
        return (m_X);
      }
      double getY()
      {
        return (m_Y);
      }
    private:
      double m_X;
      double m_Y;
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Bar
    {
      double getValue()
      {
        return (m_value);
      }
    private:
      double m_value;
    };

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    double

    Tout est dit.

    Tu es, toi aussi, victime de l'approximation flottante.

    Certaines corrections anti-erreurs sont applicables sur le cas des variables locales, ce qui n'est pas forcément le cas avec les appels de fonctions.
    Ou le contraire
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    C'est tout de même bizarre. Je viens de faire le test avec le code suivant:
    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
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    struct Foo
    {
    	Foo(double x, double y) : m_X(x), m_Y(y) {}
     
    	double getX()
    	{
    		return (m_X);
    	}
     
    	double getY()
    	{
    		return (m_Y);
    	}
     
    	double m_X, m_Y;
    };
     
    struct Bar
    {
    	Bar(double value) : m_value(value) {}
     
    	double getValue()
    	{
    		return (m_value);
    	}
     
    	double m_value;
    };
     
    int main(int argc, char* argv[])
    {
    	Foo foo(2.3, 3.2);
    	Bar bar(5.4);
    	double result1 = (foo.getX() - bar.getValue()/2 + foo.getY()/2)/foo.getX();
     
    	double x = foo.getX();
    	double value = bar.getValue();
    	double y =  foo.getY();
    	double result2 = (x - value/2 + y/2)/x;
     
    	getchar();
    	return 0;
    }
    et dans mon test, result1 et result2 sont exactement égaux, avec une visualisation à 17 chiffres après la virgule (je ne sais pas comment en voir plus). Windows 7, visual c++ 2010.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  6. #6
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    Je connais le problème de l'approximation flottante (due à la manière de representer les flottants) mais là j'ai du mal à accepter la différence de resultat.

    Si je t'avais dit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    double x = 3.0;
    if((1./3.)==(1./x))
    {
     //
    }
    else
    {
     // approximation flottante
    }
    // La OK, je comprends
    Mais là tu es entrain de me dire que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    double x = getX();
    if(x != getX())
    {
      // tout est normal: approximation flottante
    }

  7. #7
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    r0d:
    Essaye une fois avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    x = 343.41000000000002501110429875552654;
    y =  23.70134488207620293565014435444027;
    value = 33.92;
    S'il te plait.

  8. #8
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    J'obtiens:
    result1 0.98512178574018849
    result2 0.98512178574018849

    Je ne sais pas comment avoir plus de précision avec vc2010.
    Quel est ton environnement de dev (os, compilateur)?
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  9. #9
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Je pense que ça vient du fait que dans le second cas tu passes par des variables intermédiaires. Donc arrondies une fois supplémentaire. Puis le calcul se fait sur les valeurs arrondies, alors que dans le premier il doit pouvoir utiliser des valeurs plus "exacts".
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  10. #10
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Linux xubuntu 3.5.0-18-generic #29-Ubuntu SMP Fri Oct 19 10:27:31 UTC 2012 i686 i686 i686 GNU/Linux
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
    Je suis tenté de dire : "peut importe l'environnement", car il s'agit du même code executé dans le même envirronement qui produit deux résultats différents selon que je passe par des variables intermédiares ou pas.

  11. #11
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    r0d:
    En reprenant ton code:
    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
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include <iostream>
     
    struct Foo
    {
    	Foo(double x, double y) : m_X(x), m_Y(y) {}
     
    	double getX()
    	{
    		return (m_X);
    	}
     
    	double getY()
    	{
    		return (m_Y);
    	}
     
    	double m_X, m_Y;
    };
     
    struct Bar
    {
    	Bar(double value) : m_value(value) {}
     
    	double getValue()
    	{
    		return (m_value);
    	}
     
    	double m_value;
    };
     
    int main(int argc, char* argv[])
    {
        	std::cout.setf(std::ios::fixed, std::ios::floatfield);
    	std::cout.precision(32);
    	Foo foo(343.41000000000002501110429875552654, 23.70134488207620293565014435444027);
    	Bar bar(33.92);
    	double result1 = (foo.getX() - bar.getValue()/2 + foo.getY()/2)/foo.getX();
     
    	double x = foo.getX();
    	double value = bar.getValue();
    	double y =  foo.getY();
    	double result2 = (x - value/2 + y/2)/x;
     
    	std::cout<<"\n\t res1 = "<<result1<<"\n\t res2 = "<<result2<<"\n";
     
    	return 0;
    }
    Voilà ce que j'obtiens:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	 res1 = 0.98512178574018849364080097075203
    	 res2 = 0.98512178574018838261849850823637

  12. #12
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Juste pour l'expérience, essaie avec des méthodes retournant des "const double&", vu que tu as les membres.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  13. #13
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    Citation Envoyé par leternel Voir le message
    Juste pour l'expérience, essaie avec des méthodes retournant des "const double&", vu que tu as les membres.
    Ca ne change rien, j'obtiens la même différence.

  14. #14
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par LinuxUser Voir le message
    Je suis tenté de dire : "peut importe l'environnement",
    Je demandais ça parce qu'en fait, ta compilation peut être customisée, notamment en ce qui concerne la gestion des flottants. Et je voulais savoir si tu utilisais visual, car dans ce cas j'aurais pu te donner quelques tips. En revanche, je ne connais pas assez gcc pour t'aider sur ce point.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  15. #15
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    Citation Envoyé par r0d Voir le message
    Je demandais ça parce qu'en fait, ta compilation peut être customisée, notamment en ce qui concerne la gestion des flottants. Et je voulais savoir si tu utilisais visual, car dans ce cas j'aurais pu te donner quelques tips. En revanche, je ne connais pas assez gcc pour t'aider sur ce point.
    Maintenant que tu me le dis, en effet j'utilise une option d'optimisation
    qui optimise la taille.

    Mais pour tester ton exemple, j'ai compilé sans option et la différence est tout de même apparue.

  16. #16
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Personnellement, je me base sur le principe de base suivant:
    "deux flottants ne sont jamais égaux"

    Du coup, je ne me pose pas trop la question.
    Le jour où je veux plus d'exactitude … n'est pas encore arrivé.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  17. #17
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Sur gcc ubuntu
    res1 = 0.98512178574018849364080097075203
    res2 = 0.98512178574018849364080097075203
    Peux tu donner le code asm ? (ajoute -S à la compilation)

    @Leternel
    C'est un peu strict comme règle. Oui, on doit faire attention en utilisant les réels. Mais 2 calculs identiques doivent donner le même résultat

  18. #18
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Au fait, sur quelle machine tu fais tourner ?

  19. #19
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    En compilant avec -S, j'obtiens à 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
    ./main: ligne 1: .file : commande introuvable
    ./main: ligne 2: .section : commande introuvable
    ./main: ligne 3: .weak : commande introuvable
    ./main: ligne 4: .type : commande introuvable
    ./main: ligne 5: _ZStanSt13_Ios_FmtflagsS_: : commande introuvable
    ./main: ligne 6: .LFB585: : commande introuvable
    ./main: ligne 7: .cfi_startproc : commande introuvable
    ./main: ligne 8: pushl : commande introuvable
    ./main: ligne 9: .cfi_def_cfa_offset : commande introuvable
    ./main: ligne 10: .cfi_offset : commande introuvable
    ./main: ligne 11: movl : commande introuvable
    ./main: ligne 12: .cfi_def_cfa_register : commande introuvable
    ./main: ligne 13: Erreur de syntaxe près du symbole inattendu « ( »
    ./main: ligne 13: `	movl	8(%ebp), %edx'
    Ce qui est bizarre, c'est qu'il n'y a pas d'affichage des resultats.

  20. #20
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Normal, ça génère le code asm dans le fichier .o et ensuite gcc essaie de continuer la compilation comme si le .o contenait vraiment... un .o
    Il faut ouvrir le .o avec un éditeur de text

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 3 123 DernièreDernière

Discussions similaires

  1. Réaliser un calcul avec variable
    Par remi2305 dans le forum Macros et VBA Excel
    Réponses: 12
    Dernier message: 12/03/2015, 10h16
  2. [2.x] [twig] Déclaration et calcul avec variables
    Par jejeman dans le forum Symfony
    Réponses: 3
    Dernier message: 30/01/2014, 12h27
  3. [CR XI] Calcul avec variable partagée
    Par sat478 dans le forum SAP Crystal Reports
    Réponses: 7
    Dernier message: 18/09/2010, 16h14
  4. Réponses: 3
    Dernier message: 16/07/2008, 16h28
  5. Formule de calcul avec variable ?
    Par clarisse dans le forum Excel
    Réponses: 5
    Dernier message: 19/09/2007, 14h27

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