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 :

Précision en C++


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Février 2012
    Messages : 16
    Par défaut Précision en C++
    Bonjour, je développe un programme de calcul matriciel.
    Je m'aide de maple pour vérifier mes resultats.
    Cependant, pour certains calculs j'obtiens des résultats pour le moins étranges.

    Petit exemple :
    A=
    0 1 1 1
    0 -2 -1 1
    0 -1 1 0
    -1 1 0 -1

    Inv(A) =
    -0.4 -0.6 -0.2 -1
    0.2 -0.2 -0.4 0
    0.2 -0.2 0.6 0
    0.6 0.4 -0.2 0
    Si je fais les opérations à la main je trouve bien :
    A.Inv(A) =
    1 0 0 0
    0 1 0 0
    0 0 1 0
    0 0 0 1

    par exemple :
    (1;1) => 1 = 0*(-0.4) + 1*(0.2) + 1*(0.2) + 1*(0.6)
    (3;2) => 0 = 0*(-0.6) + (-1)*(-0.2) + 1*(-0.2) + 0*(0.4)
    Pour l'instant pas de soucis
    Sauf que quand c'est mon programme qui fait le calcul j'obtient :
    A.Inv(A) =
    1 0 5.55112e-17 0
    0 1 -5.55112e-17 0
    0 0 1 0
    0 5.55112e-17 0 1
    et sur maple :


    Donc, première conclusion : Vive le calcul à la main
    Et deuxième... Qu'est ce qu'il se passe ici ?

    Je dois avouer que je ne sais pas d'ou viens l'erreur, si nécessaire je peux montrer mon code, mais je ne suis pas sur que l'erreur vienne de celui-ci ...

    Enfin si quelqu'un a une idée, je suis preneur !

    Merci d'avance

    PS: je viens de voir que pour des matrices plus petites ça ne le fait jamais et la probabilité d'erreur augmente avec la taille de la matrice ...

  2. #2
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Bonjour

    Il n'y a pas d'erreur (a priori), c'est juste un problème de représentation des nombres réels en informatique. Lorsque tu as 5.55112e-17, c'est à dire 0,000000...000555 (avec 17 zéros), on peut considérer que ça fait... 0
    Donc A.inv(A) est bien :
    1 0 0 0
    0 1 0 0
    0 0 1 0
    0 0 0 1
    Le mieux est probablement d'arrondir les chiffres lors de l'affichage à quelques décimales (1 ou 2)

  3. #3
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Février 2012
    Messages : 16
    Par défaut
    D'accord, dans ce cas, dans ma classe, il est préférable que je fasse l'arrondi de quelle manière ?

    Edit: En plus, je ne vois pas pourquoi maple fait aussi l'erreur, alors que ce logiciel fait des calculs très précis je trouve aussi des approximations de ce genre ...

  4. #4
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut
    Plusieurs solutions :

    Soit un cast vers l'entier le plus proche, si il est vraiment proche :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    double my_round(double value) {
       static const double epsilon = 1e-16; // A definir comme tu preferes
       const int rounded = static_cast<int>(value);
       if (v - rounded > 0.5) ++rounded;
       // rounded contient l'entier le plus proche. Il est aussi possible d'utiliser std::round de <cmath> pour faire ça, pour ce message je n'ai simplement pas pris le temps de vérifier le prototype exact.
       double error = rounded - value;
       if (-epsilon < error && error < epsilon) // Si on est proche de l'entier le plus proche
          return rounded; // On arrondit vers lui
       else // Sinon
          return value; // On ne change rien
    }
    Soit on fait du calcul formel, qui a l'avantage d'être infiniment précis (autant que le calcul à la main sans arrondi, quoi) -- mais là, bon courage !

  5. #5
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Lors de l'affichage avec cout, tu peux préciser la précision souhaitée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    double f = 3.14159;
    cout.unsetf(ios::floatfield);            // floatfield not set
    cout.precision(5);
    cout << f << endl;
    voir http://www.cplusplus.com/reference/i...ase/precision/

    Citation Envoyé par Ekleog Voir le message
    Plusieurs solutions :

    Soit un cast vers l'entier le plus proche, si il est vraiment proche :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    double my_round(double value) {
       static const double epsilon = 1e-16; // A definir comme tu preferes
       const int rounded = static_cast<int>(value);
       if (v - rounded > 0.5) ++rounded;
       // rounded contient l'entier le plus proche. Il est aussi possible d'utiliser std::round de <cmath> pour faire ça, pour ce message je n'ai simplement pas pris le temps de vérifier le prototype exact.
       double error = rounded - value;
       if (-epsilon < error && error < epsilon) // Si on est proche de l'entier le plus proche
          return rounded; // On arrondit vers lui
       else // Sinon
          return value; // On ne change rien
    }
    Soit on fait du calcul formel, qui a l'avantage d'être infiniment précis (autant que le calcul à la main sans arrondi, quoi) -- mais là, bon courage !
    Il vaut mieux utiliser les fonctions d'arrondis, non ?
    floor et ceil

  6. #6
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Février 2012
    Messages : 16
    Par défaut
    Ekleog : peux-tu détailler un peu cette fonction s'il te plait ? Parce que j'aimerai n'utiliser que des fonctions "handmade"

    gbdivers : en fait c'est surtout la valeur qui m'interesse, l'affichage après si la valeur est "exacte" (enfin si ça existe ) devrait aller

  7. #7
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Bonjour,

    1/ Les arrondis ca se fait uniquement sur l'affichage. Les incertitudes de mesure/calcul ca se détermine/propage via des forumles, si tu commences à arrondir (*) de partout ca n'est plus possible.

    2/ A l'affichage tu peux décider d'arrondir à une précision donnée, mais utiliser un affichage du type A +/- delta, est tout aussi correct.

    3/ Si tu veux faire quelque chose d'efficase tu n'auras pas le choix de passer par les expressions templates, et avec ca ton problème va se simplifier.

    Avec les expressions templates tu vas obtenir un arbre représentant ton expression, arbre que tu évalueras avec () (en général), il est (probablement) possible lors de l'évaluation de vérifier si il y a une expression du type mult<T,inv<U> >, et si c'est le cas de comparer (à epsilon près avec un epsilon bien déterminé : propagation des erreurs) l'évaluation de l'expression T et de l'expression U, et si elles sont "égales" de retourner l'identité.

    Je vais essayer de poster un code "from scratch" qui montre l'idée, mais tu peux déjà aller regarder du coté de boost::proto, il donne des exemples liés au calcul algébrique.

    (*) Pour être exact, la valeur retenue doit être cohérante avec l'incertitude, ca peut amener certains arrondies/troncatures, mais on arrondie pas dans le but de compenser l'incertitude.

    PS: Personnelement, si tu débutes en C++ et que tu n'ambionnes pas quelque chose de très efficase, je me contenterais de garder dans ma classe matrice quelque chose représentant l'incertitude, et ferais une fonction d'affichage à une précision donnée ou alors un affichage de type A +/- delta.

  8. #8
    Membre expérimenté Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Par défaut
    Il n'y a pas d'erreur, mais les types a virgule flottante (float, double) ne sont pas précis. Tu auras régulièrement des erreurs dans le genre, il faut savoir les anticiper, elles sont inévitables.
    Mais franchement, a 10^-17, ca devrait le faire.

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

Discussions similaires

  1. Précision sur Oracle 9iAS r2
    Par Patmane dans le forum Oracle
    Réponses: 9
    Dernier message: 18/03/2007, 04h41
  2. [3DNow !] Précision
    Par delire8 dans le forum x86 32-bits / 64-bits
    Réponses: 11
    Dernier message: 19/02/2004, 19h10
  3. [EJB]Précision de la datasource (Mysql)
    Par cameleon2002 dans le forum JBuilder
    Réponses: 2
    Dernier message: 11/09/2003, 17h55
  4. Réponses: 8
    Dernier message: 13/01/2003, 17h45
  5. Timer de précision
    Par guigui dans le forum MFC
    Réponses: 1
    Dernier message: 04/12/2002, 15h21

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