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 :

Precision des long double


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    inge info
    Inscrit en
    Juin 2010
    Messages
    34
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : inge info

    Informations forums :
    Inscription : Juin 2010
    Messages : 34
    Points : 40
    Points
    40
    Par défaut Precision des long double
    Bonjour à tous !


    Alors aujourd'hui parlons double

    Je boss sur un programme qui utilise des long double avec plein de chiffres (en gros on est sur du 10^(-20)) mais j'ai des problèmes. Alors ces long double servent à calculer la position d'éléments et je vois bien que ces éléments ne sont pas là où ils le devraient quand ils le devraient.

    Après avoir perdu 3 jours a vérifier l'ensemble des arguments et tout ce qui m'est passés par la tête pouvant être a l'origine de ce mauvais placement je décide de faire un truc tout bête :

    j'ai un long double qui vaut : 0.08749837479571349900

    je decide de faire +0.01 pour voir le résultat et la j'obtiens

    0.09749837479571348400


    alors vous allez me dire que la différence est minime ce qui est généralement vrai mais c'est pas le cas dans le domaine dans lequel s'applique ce programme.

    Quelqu'un aurait il une idée pour régler ca?

    Merci pour ceux qui m'aideront à comprendre le pourquoi du comment

  2. #2
    Membre émérite Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 047
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 047
    Points : 2 254
    Points
    2 254
    Par défaut
    Quelqu'un aurait il une idée pour régler ca?
    Régler quoi? c'est quoi le problème exactement? car la on sais que tu as un double à 0.08 et tu rajoute +0.01 et ça fait 0.09... mais bon apparemment il s'agit d'une erreur de calcul ou sinon on veux le code qui te sert a faire tes calculs :p

  3. #3
    Membre du Club
    Profil pro
    inge info
    Inscrit en
    Juin 2010
    Messages
    34
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : inge info

    Informations forums :
    Inscription : Juin 2010
    Messages : 34
    Points : 40
    Points
    40
    Par défaut
    le probleme c'est la fin du chiffre :


    9900 devient 8400 et c'est très genant

  4. #4
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921

  5. #5
    Membre expérimenté
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Points : 1 685
    Points
    1 685
    Par défaut
    Bonsoir,

    Citation Envoyé par CPT_Taverne Voir le message
    Alors ces long double servent à calculer la position d'éléments et je vois bien que ces éléments ne sont pas là où ils le devraient quand ils le devraient.
    Il faudrait que tu précises un petit peu ce que tu appelles la position d'un élément.
    S'agit-il de coordonnées de points?
    Explique un peu le contexte, ça sera plus facile de te répondre.


    Citation Envoyé par CPT_Taverne Voir le message
    Après avoir perdu 3 jours a vérifier l'ensemble des arguments et tout ce qui m'est passés par la tête pouvant être a l'origine de ce mauvais placement je décide de faire un truc tout bête :
    j'ai un long double qui vaut : 0.08749837479571349900
    je decide de faire +0.01 pour voir le résultat et la j'obtiens
    0.09749837479571348400
    Comme l'indique Joel F avec son lien, c'est normal.

    Citation Envoyé par CPT_Taverne Voir le message
    alors vous allez me dire que la différence est minime ce qui est généralement vrai mais c'est pas le cas dans le domaine dans lequel s'applique ce programme.
    Il faudrait que tu expliques pourquoi.
    On peut parfois mal interpréter ce genre de phénomène.
    Quelques exemples sont donnés dans le paragraphe 1.19 (page 28) de ce livre :
    http://books.google.fr/books?id=epil...page&q&f=false

    En fait, tout dépend de la manière dont se propage tes erreurs d'arrondi.
    Et cela dépend directement de ton algorithme.

  6. #6
    Membre régulier
    Inscrit en
    Août 2010
    Messages
    68
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 68
    Points : 79
    Points
    79
    Par défaut
    Hello,

    Selon tes besoins, tu peux regarder des "astuces" telles que la sommation de Kahan et t'en inspirer pour simuler une précision encore plus importante.
    Par contre, ça devient intéressant qu'à partir de pas mal d'opérations, et c'est pas forcément cool à mettre en oeuvre (optimisations du compilo, portabilité...).

    Meilleure solution, utiliser une librairie qui permet une précision qui dépasse les besoins humains et extraterrestres : MPFR
    (qui n'a jamais rêvé de coder un nombre sur 1 kilo octet ? )

  7. #7
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    du moment ou il fait +0.01 qui n'a pas de representation parfaite en base2, c'ets mort ...

    Et j'aimerais bien connaitre le domaine d'appli ou une erreur de positionnement de 10 attometre est grave ...

  8. #8
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 410
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 410
    Points : 23 808
    Points
    23 808
    Par défaut
    Citation Envoyé par Joel F Voir le message
    Et j'aimerais bien connaitre le domaine d'appli ou une erreur de positionnement de 10 attometre est grave ...
    C'est en général une erreur de rapporter le calcul de précision à la mesure des distances (même si ça a l'air d'être son cas), justement parce qu'on a tendance à conclure un peu trop vite que, dans ce cas précis, ça n'a pas d'importance.

    Ça devient gênant lorsque ces nombres sont impliqués dans des progressions géométriques et que les erreurs se cumulent. Avec une raison à peu près égale à 2, l'imprécision progresse d'un bit à chaque itération si elle n'est pas contrôlée, et on comprend que passé 50 itérations, même la partie entière peut être affectée.

    Ça a d'ailleurs l'air d'être son cas, puisqu'il a relevé des positions irrégulières avant de mettre le doigt sur leur origine.

  9. #9
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    J'ai fait le raccourci justement à cause de cette histoire de position

    Neanmoins, l'erreur est de l'ordre de Eps<double>() donc bon ...

  10. #10
    Invité
    Invité(e)
    Par défaut
    Bonjour,
    Ce sujet m'avait échappé, je le découvre pas hasard grâce à un lien de lien de lien.
    La réponse est très simple.
    Un flottant (32 bits) a une précision de 7 chiffres.
    Un double (64 bits) a une précision de 15 chiffres.
    Un long double (80 bits) a une précision de 19 chiffres.
    Le nombre indiqué est le nombre de chiffres significatifs, c'est à dire la mantisse. La position du point décimal est indépendante.
    C'est à dire qu'en général un ne peut pas (ou il ne faut pas) tester l'égalité de deux flottants (float, double, ou long double)
    En conclusion si vous utilisez le long double en pensant être plus sûr du résultat, vous avez tout faux.
    Toute autre explication est parfaitement fausse.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 632
    Points : 30 708
    Points
    30 708
    Par défaut
    Salut,
    Citation Envoyé par Pierre Dolez Voir le message
    Bonjour,
    Ce sujet m'avait échappé, je le découvre pas hasard grâce à un lien de lien de lien.
    La réponse est très simple.
    Un flottant (32 bits) a une précision de 7 chiffres.
    Un double (64 bits) a une précision de 15 chiffres.
    Un long double (80 bits) a une précision de 19 chiffres.
    Sur ta machine à toi, avec ton compilateur à toi...
    On ne peut, à la base, absolument pas présumer de la taille des différents types, et donc, encore moins, présumer de la précision qu'ils peuvent apporter.

    Je ne dis absolument pas que les valeurs que tu donnes sont fausses, je dis juste qu'elles ne sont valables que dans un domaine particulier qui est celui des PC

    D'autres systèmes peuvent parfaitement avoir des float à 80 bits et des long double à l'avenant
    Citation Envoyé par Pierre Dolez Voir le message
    Le nombre indiqué est le nombre de chiffres significatifs, c'est à dire la mantisse. La position du point décimal est indépendante.
    C'est à dire qu'en général un ne peut pas (ou il ne faut pas) tester l'égalité de deux flottants (float, double, ou long double)
    Là dessus, on est d'accord
    Citation Envoyé par Pierre Dolez Voir le message
    En conclusion si vous utilisez le long double en pensant être plus sûr du résultat, vous avez tout faux.
    Toute autre explication est parfaitement fausse.
    Là par contre, je ne peux plus être d'accord avec toi:

    Il faut comprendre que, quelle que soit la base utilisée, il y a des valeurs qui ne peuvent être représentée de manière finie: avec la base décimale, il est impossible de fournir une valeur finie pour 1/3 ou pour PI, ce qui fait que l'on s'arrête souvent à 0.3333333 ou à 3.1415926, par exemple.

    Lors de la conversion d'une valeur décimale en valeur binaire, et à cause de la méthode de conversion, on se retrouve confronté au même problème qui fait que 0.1 en décimal ne pourra pas être représenté de manière finie en binaire, surtout si c'est une valeur calculée.

    A partir de là, tout peut arriver, selon la manière dont 0.1 sera effectivement représenté en binaire par le processeur

  12. #12
    Invité
    Invité(e)
    Par défaut
    Bonjour Koala.
    Concernant le nombre de chiffres significatifs, sauf erreur de ma part c'est la norme C/C++, mais quelle que soit leur valeur c'est un nombre fini.

    Le seul problème important revient à se poser la question de savoir s'il convient ou non de comparer des flottants avec l'opérateur '=='. Ma réponse est NON.

    Le document : What Every Computer Scientist Should Know About Floating-Point Arithmetic explique cela très en détail, je répéterai 2 exemples cités dans un autre sujet
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    float A=3.0/7.0;
    if (A == 3.0/7.0) printf("BON\n");
    else  printf("FAUX\n");
    et
    On veut calculer A=1./(x-y),
    Il ne faut pas tester
    if (x != y) ...
    mais if ((x-y) != 0) ...
    C'est pas moi qui le dit

    Naturellement, pour éviter ce type de discussion stérile (maintenant je Candide officiel), je pense qu'il serait bon de mettre les choses au point une fois pour toutes, puisque la question revient tout de même assez souvent.

  13. #13
    Membre expérimenté
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Points : 1 685
    Points
    1 685
    Par défaut
    Bonsoir,

    Citation Envoyé par Pierre Dolez Voir le message
    On veut calculer A=1./(x-y),
    Il ne faut pas tester
    if (x != y) ...
    mais if ((x-y) != 0) ...
    C'est pas moi qui le dit
    Dans cet exemple, l'auteur du document que vous citez explique que, du fait qu'une erreur est potentiellement introduite dans le calcul de x-y, les deux tests
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if ((x-y) != 0) ...
    ne sont pas équivalents.
    Pour le calcul de A=1./(x-y), c'est le dernier qui permet de vérifier s'il y a division par zéro ou non.

    L'auteur ne prend pas parti sur la pertinence des comparaisons de flottants.

  14. #14
    Invité
    Invité(e)
    Par défaut
    Il doit y avoir un problème de compréhension.
    Si l'auteur dit
    il ne faut pas faire
    mais, il faut faire
    s'il ne prend pas partie, que fait-il alors ?
    Il est vrai que j'ai résumé ce qu'il disait, mais le mieux est tout de même d'y aller voir par vous-même.

    Je explique probablement trè mal les choses, donc je reprend à partir de l'exemple décrit pas le lien de Joel_F
    Dans un programme on a deux objet dont les valeurs peuvent s'additionner.
    O1 et O2.
    O1 vaut 0.1, et O2 vaut 0.2.
    Si l'addition des valeurs vaut 0.3, alors on a gagné.
    Donc le programme va faire S=v(0.1) + v(0.2)
    Puis pour voir si on a gagné, on test
    if ( S == 0.3) ...
    Je vous laisse deviner la suite.
    Dernière modification par Invité ; 04/09/2010 à 20h55.

  15. #15
    Membre expérimenté
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Points : 1 685
    Points
    1 685
    Par défaut
    Bonsoir,

    Citation Envoyé par Pierre Dolez Voir le message
    Il doit y avoir un problème de compréhension.
    C'est certain.

    Citation Envoyé par Pierre Dolez Voir le message
    Si l'auteur dit
    il ne faut pas faire
    mais, il faut faire
    s'il ne prend pas partie, que fait-il alors ?
    Il explique l'introduction potentielle d'un bogue lorsqu'on ne sait pas que le calcul de x-y n'est pas nécessairement exact.


    Citation Envoyé par Pierre Dolez Voir le message
    Il est vrai que j'ai résumé ce qu'il disait, mais le mieux est tout de même d'y aller voir par vous-même.
    Lorsqu'on va voir par soi-même, on lit ceci (désolé pour les symboles latex non reproduits, cf l'original) :

    Consider normalized floating-point numbers with = 10, p = 3, and emin = -98. The numbers x = 6.87 × 10-97 and y = 6.81 × 10-97 appear to be perfectly ordinary floating-point numbers, which are more than a factor of 10 larger than the smallest floating-point number 1.00 × 10-98. They have a strange property, however: x y = 0 even though x y! The reason is that x - y = .06 × 10 -97 = 6.0 × 10-99 is too small to be represented as a normalized number, and so must be flushed to zero. How important is it to preserve the property

    (10) x = y x - y = 0 ?

    It's very easy to imagine writing the code fragment, if (x y) then z = 1/(x-y), and much later having a program fail due to a spurious division by zero. Tracking down bugs like this is frustrating and time consuming. On a more philosophical level, computer science textbooks often point out that even though it is currently impractical to prove large programs correct, designing programs with the idea of proving them often results in better code. For example, introducing invariants is quite useful, even if they aren't going to be used as part of a proof. Floating-point code is just like any other code: it helps to have provable facts on which to depend. For example, when analyzing formula (6), it was very helpful to know that x/2 < y < 2x x y = x - y. Similarly, knowing that (10) is true makes writing reliable floating-point code easier. If it is only true for most numbers, it cannot be used to prove anything.

    The IEEE standard uses denormalized18 numbers, which guarantee (10), as well as other useful relations. They are the most controversial part of the standard and probably accounted for the long delay in getting 754 approved. Most high performance hardware that claims to be IEEE compatible does not support denormalized numbers directly, but rather traps when consuming or producing denormals, and leaves it to software to simulate the IEEE standard.19 The idea behind denormalized numbers goes back to Goldberg [1967] and is very simple. When the exponent is emin, the significand does not have to be normalized, so that when = 10, p = 3 and emin = -98, 1.00 × 10-98 is no longer the smallest floating-point number, because 0.98 × 10-98 is also a floating-point number.

    There is a small snag when = 2 and a hidden bit is being used, since a number with an exponent of emin will always have a significand greater than or equal to 1.0 because of the implicit leading bit. The solution is similar to that used to represent 0, and is summarized in TABLE D-2. The exponent emin is used to represent denormals. More formally, if the bits in the significand field are b1, b2, ..., bp -1, and the value of the exponent is e, then when e > emin - 1, the number being represented is 1.b1b2...bp - 1 × 2e whereas when e = emin - 1, the number being represented is 0.b1b2...bp - 1 × 2e + 1. The +1 in the exponent is needed because denormals have an exponent of emin, not emin - 1.

    Recall the example of = 10, p = 3, emin = -98, x = 6.87 × 10-97 and y = 6.81 × 10-97 presented at the beginning of this section. With denormals, x - y does not flush to zero but is instead represented by the denormalized number .6 × 10-98. This behavior is called gradual underflow. It is easy to verify that (10) always holds when using gradual underflow.

  16. #16
    Invité
    Invité(e)
    Par défaut
    Oh, bug ou pas dans la norme IEEE, la seule chose qui m'intéresse est que quand je veux comparer 2 flottants, je veux être sûr de la pertinence du résultat. C'est peut-être ce qui distingue les mathématiciens des praticiens et l'une des raisons pour lesquelles certains logiciels sont des usines à gaz, mais je vous laisse continuer à donner des conseils éclairés. C'est moi qui renonce.
    Autrement dit si vous testez (x != y) au lieu de ((x-y) !=0) et si ça plante, la réponse sera :"C'est pas moi, c'est un bug IEEE !).

    Au risque de me répéter, je vais essayer de continuer mon argumentation.

    Les réels sont non dénombrables, le flottants sont dénombrables. Il ne peut donc pas exister de correspondance biunivoque entre un réel et un flottant. Les machines travaillent avec des flottants, c'est à dire que la valeur de flottant peut être l'expression d'une infinité de réels.
    Les normes ont fixé, pour chaque type de flottant, le nombre de chiffres significatifs à garantir.

    Ci-dessous un petit 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
     
    int main()
    {
      union Nombre
      {
        float n;
        char *N;
      };
      Nombre A1;
      A1.n=0.1;
      Nombre A2; A2.n=1.E-1;
      Nombre B;  B.n=0.2;
      Nombre C;  C.n=0.3;
      printf("A1=%8X  A2=%8X  B=%8X  C=%8X\n",A1.N, A2.N ,B.N, C.N);
      Nombre res;
      res.n=(A1.n + B.n);
      if (res.n == 0.3) printf("A1+B Bon\n");
                    else printf("A1+B FAUX %0.20f  res= %8X\n",(A1.n + B.n),res.N);
      res.n=(A2.n + B.n);
      if ( res.n == 0.3) printf("A2+B Bon\n");
                    else printf("A2+B FAUX %0.20f  res= %8X\n",(A2.n + B.n),res.N);
      if ( res.n == C.n) printf("A2+B == C Bon\n");
                    else printf("A2+B FAUX %0.20f  res= %8X\n",(A2.n + B.n),res.N);
      system("Pause");
      return 0;
    }
    La seule conclusion que j'arrive à en tirer est qu'IL NE FAUT PAS comparer des flottants avec l'opérateur '=='.
    Sur ma machine le nombre de chiffres significatifs justes est 8, donc la précision de 7 chiffres pour un float est bien respectée, il n'y a pas d'erreur d'arrondi ou de choses de ce genre.

    @Kaola pour info, les résultats sont les mêmes sur une machine travaillant sous Linux et le compilateur probablement gcc, en tout cas par le même que le mien. Et chez vous, qu'est-ce que ça donne?
    Dernière modification par Invité ; 06/09/2010 à 11h31.

  17. #17
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 206
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 206
    Points : 12 375
    Points
    12 375
    Par défaut
    Je pense que tous les intervenants savent de quoi il en retourne avec les problèmes de représentation des floats.

    Même si la norme C++ ne précise pas le format, il y a la norme IEEE respecter par beaucoup de CPU et que les compilateurs s'aligne sur les spécifications des CPU.

    Arrêtons de couper les cheveux en 4. "==" avec des floats, c'est la porte des enfers.

  18. #18
    Membre régulier Avatar de Chessmaster1966
    Inscrit en
    Juillet 2010
    Messages
    63
    Détails du profil
    Informations forums :
    Inscription : Juillet 2010
    Messages : 63
    Points : 74
    Points
    74
    Par défaut
    Un long double n'a qu'une précision de 7 chiffres après la virgule.
    Si on ajoute 0.01 à cette valeur : 0.0034561 on obtient 0.1334561 et non
    0.0034562 pour obtenir cette dernière il faudrait faire cela : 0.0000001 et le tour est joué.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
       long double x = 0.0034561;
       cout << x << '\n'; //Affiche : 0.0034561
       x += 0.0000001;
       cout << x << '\n'; //Affiche : 0.0034562
    La mantisse à une certaine taille si on l'a dépasse il y a troncature, donc perte de précision.

    Dans le premier exemple tu ajoute 1 à la deuxième décimale si tu veux ajouter 1 à la cinquième décimale alors tu fais ça : 0.00001 et ainsi de suite.

    Dans cet exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
       long double x = 0.1234561;
       cout << x << '\n'; //Affiche : 0.123456
       x += 0.0000001;
       cout << x << '\n'; //Affiche : 0.123456
    Cela démontre bien ce que je viens dire. Le nombre 0.1234561 est trop grand pour la mantisse et si on ajoute 0.0000001 cela se fait dans le vide. Du coup on perd une décimale.

    J'espère que j'ai été assez claire.

  19. #19
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 410
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 410
    Points : 23 808
    Points
    23 808
    Par défaut
    Bonsoir,

    Citation Envoyé par Pierre Dolez Voir le message
    C'est à dire qu'en général un ne peut pas (ou il ne faut pas) tester l'égalité de deux flottants (float, double, ou long double)
    En conclusion si vous utilisez le long double en pensant être plus sûr du résultat, vous avez tout faux. Toute autre explication est parfaitement fausse.
    Ton affirmation, tout comme le ton que tu emploies, sont un peu trop péremptoires à mon goût.

    Citation Envoyé par koala01 Voir le message
    Salut,
    Sur ta machine à toi, avec ton compilateur à toi...
    On ne peut, à la base, absolument pas présumer de la taille des différents types, et donc, encore moins, présumer de la précision qu'ils peuvent apporter.

    Je ne dis absolument pas que les valeurs que tu donnes sont fausses, je dis juste qu'elles ne sont valables que dans un domaine particulier qui est celui des PC

    D'autres systèmes peuvent parfaitement avoir des float à 80 bits et des long double à l'avenant
    Je ne sais pas ce qu'en dit la norme C++, mais la norme C (en tout cas le dernier draft) précise que ses flottants sont ceux d'IEC 60559, soit IEEE 754, et cette norme fixe la taille de ces flottants (simple précision, double précision).

    Citation Envoyé par WG14/N1256 ISO/IEC 9899:TC3
    Annex F
    (normative)
    IEC 60559 floating-point arithmetic
    F.1 Introduction
    1 This annex specifies C language support for the IEC 60559 floating-point standard. The
    IEC 60559 floating-point standard is specifically Binary floating-point arithmetic for
    microprocessor systems, second edition (IEC 60559:1989), previously designated
    IEC 559:1989 and as IEEE Standard for Binary Floating-Point Arithmetic
    (ANSI/IEEE 754−1985).[…]

    Donc, en C, la taille des différents flottants est définie par la norme, contrairement aux entiers en binaire naturel. Ce serait étonnant que le C++ n'ait pas défini quelque chose de similaire, ou se soit aligné sur le C. À vérifier, donc.

    Citation Envoyé par Pierre Dolez Voir le message
    Le seul problème important revient à se poser la question de savoir s'il convient ou non de comparer des flottants avec l'opérateur '=='. Ma réponse est NON. […] C'est pas moi qui le dit
    Rien que ça ! C'est quand même violent comme prescription, tu ne crois pas ? Surtout si « ce n'est pas toi qui le dit ».

    Le document : What Every Computer Scientist Should Know About Floating-Point Arithmetic explique cela très en détail, je répéterai 2 exemples cités dans un autre sujet
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    float A=3.0/7.0;
    if (A == 3.0/7.0) printf("BON\n");
    else  printf("FAUX\n");
    Essaie de remplacer « A == 3.0/7.0 » par « (A-(3.0/7.0)) == 0 » pour voir. Tu obtiens « faux » également, et pour les mêmes raisons. Donc, si tu as remplacé toutes tes égalités par des soustractions, et que tu n'as rien résolu, tu vas engendrer plus de problèmes que tu peux espérer en résoudre.

    je pense qu'il serait bon de mettre les choses au point une fois pour toutes, puisque la question revient tout de même assez souvent.
    Oui, et c'est qu'on va s'attacher à faire. Le code ci-dessus renvoie « FAUX » alors que les valeurs constantes sont déclarées exactement de la même façon dans les deux cas. Pourquoi ? Parce que les constantes flottantes non qualifiées sont de type double par défaut. Et 3.0/7.0 en float et double se représentent comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    double : 3f db 6d b6 db 6d b6 db
    float  : 3e db 6d b7
    En simple précision, le nombre a été arrondi à sa valeur supérieure. Maintenant, que se passe-t-il lorsque l'on compare deux nombres de différent types ? C'est dans la norme :

    6.5.9 Equality operators

    Syntax
    1 equality-expression:
    relational-expression
    equality-expression == relational-expression
    equality-expression != relational-expression
    Constraints



    4 If both of the operands have arithmetic type, the usual arithmetic conversions are
    performed […]

    6.3.1.5 Real floating types

    1 When a float is promoted to double or long double, or a double is promoted
    to long double, its value is unchanged
    (if the source value is represented in the
    precision and range of its type).

    6.3.1.8 Usual arithmetic conversions

    1 Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result. For the specified operands, each operand is converted, without change of type domain, to a type whose corresponding real type is the common real type. Unless explicitly stated otherwise, the common real type is also the corresponding real type of
    the result, whose type domain is the type domain of the operands if they are the same, and complex otherwise. This pattern is called the usual arithmetic conversions:

    — First, if the corresponding real type of either operand is long double, the other operand is converted, without change of type domain, to a type whose corresponding real type is long double.

    Otherwise, if the corresponding real type of either operand is double, the other operand is converted, without change of type domain, to a type whose corresponding real type is double.

    — Otherwise, if the corresponding real type of either operand is float, the other
    operand is converted, without change of type domain, to a type whose
    corresponding real type is float.51)

    Pour résumer :

    • En présence de deux types de même nature mais de différentes taille, l'opérateur de comparaison « == » (et pas mal d'autres) va opter pour le type le plus grand, qu'il se trouve à gauche ou à droite ;
    • Notre float va donc être promu en double puisque les constantes le sont (et leur quotient, par conséquent) ;
    • Un float promu en double reste inchangé, ce qui veut dire qu'on va simplement combler le reste de la mantisse avec des zéros ;


    On se retrouve donc à comparer « 3f db 6d b6 db 6d b6 db » et « 3e db 6d b7 00 00 00 00 » qui ne sont pas égaux bit à bit et cela suffit à en déduire une inégalité car il n'y a qu'une seule façon de représenter un nombre donné dans un flottant de type donné, lui aussi.

    Ça veut dire que :

    • Il est tout-à-fait sûr de comparer des flottants de même type — et heureusement !
    • Que c'est une erreur dû à l'arrondi et à la manière dont le C (et le C++ ?) transtype implicitement les opérandes quand il a besoin de le faire.


    C'est exactement comme comparer un short et un long sans tenir compte du dépassement qui pourrait se produire sur le premier. Soit, traduit en C :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    short int x = 1000000/3;
     
    if (x == (1000000/3)) printf ("BON\n"); else printf("PAS BON\n");

    … sauf que dans ce dernier cas, la comparaison est forcément toujours fausse, le compilo est à même de le détecter et imprime un avertissement.

    Cela dit, ça méritait effectivement d'être mis en lumière car c'est un bug peu fréquent et chiant à déceler. Mais ça implique quand même de mélanger float et double.

    Quant à l'autre cas, c'est édifiant :

    On veut calculer A=1./(x-y),
    Il ne faut pas tester
    if (x != y) ...
    mais if ((x-y) != 0) ...
    C'est pas moi qui le dit
    C'est le contraire !

    Il explique que dans certains cas, « (x-y) = 0 » même si x et y ne sont pas égaux parce que la différence peut être trop petite pour être représentée. Ça veut dire que ((x-y)==0) serait vrai même si x et y ne sont pas égaux, ce qui te serait clairement indiqué par « == ».

    Ne me dis pas que tu as modifié tous tes programmes en conséquence, tout de même ?

  20. #20
    Invité
    Invité(e)
    Par défaut
    @ obsidian,
    Mon, je n'ai rien modifié pour la bonne raison que depuis 30 ans que je développe, sauf cas précis où je veux tester qu'un flottant a été initialisé à une certaine valeur ou pas, je ne teste JAMAIS l'égalité de deux flottants.
    Si x et y sont des flottants et que je dois diviser par (x - y), alors je fais
    if ((x-y) > tol) ...
    tol étant une valeur adaptée au type de valeurs concernée.

    Citation:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    On veut calculer A=1./(x-y),
    Il ne faut pas tester
    if (x != y) ...
    mais if ((x-y) != 0) ...
    C'est pas moi qui le dit
    C'est le contraire !
    Donc je traduis : moi je dis qu'il faut tester (x-y) != 0
    vous dites : non c'est le contraire il faut tester x != y
    On on effectue la division, puisque x est différent de y
    Or pas de chance (x-y) est nul, mais c'est pas ça qu'on a testé, donc on peut faire la division ... Que se passe-t-il alors?

Discussions similaires

  1. Affichage long double
    Par slate dans le forum C++
    Réponses: 5
    Dernier message: 07/02/2006, 19h04
  2. requette qui renvoi des ligne double
    Par fehmitn dans le forum PostgreSQL
    Réponses: 1
    Dernier message: 13/09/2004, 23h36
  3. Réponses: 4
    Dernier message: 12/09/2003, 12h38
  4. abs pour un long double
    Par barthelv dans le forum C
    Réponses: 2
    Dernier message: 23/07/2003, 17h16
  5. String -> long double (_strlold ?)
    Par haypo dans le forum C
    Réponses: 7
    Dernier message: 25/07/2002, 21h22

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