Partage
  • Partager sur Facebook
  • Partager sur Twitter

Les nombres à virgule flottante du point de vue de la norme

Sujet résolu
    26 juin 2011 à 13:54:41

    Bonjour,

    Je consultais la norme C99 à propos des nombres à virgule flottante, et ce que j'ai lu m'a plongé dans un abîme de perplexité...

    Ce paragraphe liste les changements entre C90 et C99 :

    Citation : ISO/IEC 9899:TC3 - Foreword, § 5 (p. xi-xii)

    This second edition cancels and replaces the first edition, ISO/IEC 9899:1990 [...]. Major changes from the previous edition include:
    [...]
    - IEC 60559 (also known as IEC 559 or IEEE arithmetic) support



    Ce texte est extrait de l'annexe F dédiée aux nombres flottants :

    Citation : ISO/IEC 9899:TC3 - Annex F (normative): IEC 60559 floating-point arithmetic (p. 444)

    F.1 Introduction

    This annex specifies C language support for the IEC 60559 floating-point standard. [...]

    F.2 Types

    The C floating types match the IEC 60559 formats as follows:
    - the float types matches the IEC 60559 simgle format.
    - the double types matches the IEC 60559 double format.
    - the long double types matches an IEC 60559 extended format, else a non-IEC 60559 extended format, else the IEC 60559 double format.
    [...]
    Recommended practice
    the long double types should match an IEC 60559 extended format.


    J'y comprend que la norme IEC 60559, autre nom de la norme IEEE 754 pour les nombres flottants, est supportée par le C99. Or, je croyais que le langage C ne garantissait pas de format pour les nombres à virgule flottante ?

    Peut-être que j'interprète un peu vite la norme, d'autant plus qu'il est écrit, dans la partie sur la représentation des types :

    Citation : ISO/IEC 9899:TC3 - (6.2.6.1) Representation of types - General, § 1 (p. 37)

    The representations of all types are unspecified except as stated in this subclause.

    Et dans la suite de l'intro de l'annexe F :

    Citation : ISO/IEC 9899:TC3 - Annex F (normative): IEC 60559 floating-point arithmetic (p. 444)

    An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex. Where a binding between the C language and IEC 60559 is indicated, the IEC 60559-specified behavior is adopted by reference, unless stated otherwise.


    Ça voudrait dire qu'en fait l'implémentation n'est pas obligée de suvre la norme IEC 60559, mais que si elle le fait elle devrait définir __STDC_IEC_559__ ?

    C'est en gros la conclusion à laquelle m'ont menées mes recherches sur Internet, mais certains sites affirment bien cependant que la norme IEC 60559 est imposée par le C99, comme ce wikilivre sur la programmation en C (j'ai mis en rouge le passage intéressant) :

    Citation : Extrait du wikilivre en question

    Les réels sont approximés par des nombres à virgule flottante. Comme dans le cas des entiers, il existe plusieurs types de nombre à virgule flottante. En voici la liste triée par précision croissante :
    - float ;
    - double ;
    - long double.

    La norme C90 était assez floue concernant les nombres à virgule flottante, leurs représentations, la précision des opérations, etc., ce qui fait que c'était un des domaines où la conception de programmes utilisant les nombres flottants était chose peu aisée. Le C99 a clarifié les choses en précisant qu'une implémentation C devait respecter la norme IEC 60559:1989 Arithmétique binaire en virgule flottante pour systèmes à microprocesseur. Cette norme (dérivée de IEEE 754) définit des formats de données pour les nombres à virgule flottante, ainsi que des opérations et fonctions sur ces nombres. Elle garantit, entre autres :
    - que certains types de données auront toujours le même format ;
    - et que les calculs effectués sur un type de donnée donneront toujours le même résultat.

    Elle définit de plus comment sont gérés les cas exceptionnels, comme les infinis, les NaN (pour Not a Number, résultant par exemple de la division 0/0), etc.

    Les types flottants du C correspondent aux type IEC 60559 de la manière suivante :
    - float : simple précision
    - double : double précision
    - long double : suivant l'implémentation, soit la double précision étendue, soit un type non-IEC 60559 (mais de précision au moins égale à double), soit double précision.



    Que faut-il donc en penser ?
    Et qu'en est-il du côté du C90 ?

    Merci d'avance de vos réponses ! :)
    • Partager sur Facebook
    • Partager sur Twitter
      26 juin 2011 à 14:59:49

      Dans un pseudo draft de la C1X(n1425) j'ai trouvé ça :

      Citation : Annex F. F.1 Introduction


      This annex specifies C language support for the IEC 60559 floating-point standard.


      Donc comme en C99.

      Mais je crois qu'elle peut admettre que IEEE 754 ne soit pas garanti.

      Citation : §5.2.4.2.2/ p.28

      20) IEC 60559:1989 specifies quiet and signaling NaNs. For implementations that do not support
      IEC 60559:1989, the terms quiet NaN and signaling NaN are intended to apply to encodings with
      similar behavior.



      PS: Quelqu'un n'aurait pas un draft au format pdf de C90 ?
      • Partager sur Facebook
      • Partager sur Twitter
      🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles  - ♡ Copying is an act of love.
        26 juin 2011 à 17:42:15

        Salut,

        Le support de la norme IEEE 754 est facultatif et l'est toujours dans la Norme C1X (comme indiqué par @che). C'est clairement dit dans la phrase que tu cites:

        Citation : Norme C99 F1 § 1 p 443


        An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex.



        D'ailleurs, si le support de cette norme était obligatoire, la macro __STDC_IEC_559__ n'aurait aucune raison d'être ;)
        • Partager sur Facebook
        • Partager sur Twitter
          26 juin 2011 à 17:47:21

          Je crois qu'il est bon de préciser : le support d'une norme veut dire simplement que la norme est... supportée, c.a.d que celui qui veut peut demander au compilo de suivre une telle norme (via la définition d'une macro), pas que le compilo est obligé de suivre cette norme.
          • Partager sur Facebook
          • Partager sur Twitter
            26 juin 2011 à 18:43:11

            Merci pour toutes vos réponses. Donc les sites qui indiquent que la norme IEC 60559 (alias IEEE 754) est "imposée" pour le C font un raccourci ?

            Citation : yoch

            Je crois qu'il est bon de préciser : le support d'une norme veut dire simplement que la norme est... supportée, c.a.d que celui qui veut peut demander au compilo de suivre une telle norme (via la définition d'une macro), pas que le compilo est obligé de suivre cette norme.

            Ça veut dire que n'importe quel compilateur doit être capable de supporter cette norme ? Et que par conséquent on peut imposer le format IEEE (par exemple) ? Héhé voilà qui m'intéresse.

            IEEE: Je ne trouve pas pour GCC. Dans la liste d'options il y en a bien qui font référence à l'IEEE mais je ne les comprend qu'à moitié et la plupart commencent par -m, ce qui indiquent qu'elles dépendent de la machine (?).
            Par exemple :

            Citation : man gcc

            These -m options are defined for the DEC Alpha implementations:
            [...]
            -mieee
            The Alpha architecture implements floating-point hardware optimized for maximum performance. It is mostly compliant with the IEEE floating point standard. However, for full compliance, software assistance is required. This option generates code fully IEEE compliant code except that the inexact-flag is not maintained (see below). If this option is turned on, the preprocessor macro "_IEEE_FP" is defined during compilation. The resulting code is less efficient but is able to correctly support denormalized numbers and exceptional IEEE values such as not-a-number and plus/minus infinity.

            (l'emphase est de moi)

            Je cherche donc une macro comme l'a précisé yoch mais je ne sais pas non plus où chercher... (_IEEE_FP ? voire peut-être __STDC_IEC_559__ ?)
            • Partager sur Facebook
            • Partager sur Twitter
              26 juin 2011 à 20:38:46

              Citation : Maelan44


              Ça veut dire que n'importe quel compilateur doit être capable de supporter cette norme ? Et que par conséquent on peut imposer le format IEEE (par exemple) ? Héhé voilà qui m'intéresse.



              Non, un compilateur n'est pas obligé de supporter la norme IEEE 754. Ce que dit la Norme C99, c'est que si ton compilateur supporte cette dernière, la constante __STDC_IEC_559__ doit valoir 1, c'est tout.

              Après, il faut voir quel format utilise ton compilateur par défaut. Du côté de GCC, c'est le format spécifié par la norme IEEE 754, tu n'as donc rien à faire. Cependant, il se pourrait très bien qu'un compilateur supporte plusieurs formats différents et en utilise un autre par défaut. Il te serait alors nécessaire de spécifier lequel tu souhaites utiliser via les arguments ;)
              • Partager sur Facebook
              • Partager sur Twitter
                30 juin 2011 à 16:11:02

                Je me permets de revenir sur le sujet.

                Tout d'abord, merci pour vos réponses.


                Citation : Taurre

                Ce que dit la Norme C99, c'est que si ton compilateur supporte cette dernière, la constante __STDC_IEC_559__ doit valoir 1, c'est tout.


                Elle doit valoir 1 ou juste être définie ?

                Et mot à mot, ce qui est dit dans la norme : SI __STDC_IEC_559__ est défini, ALORS l'implémentation utilise le format IEEE 754. Et j'ai bien l'impression que la réciproque (ce que tu as dit) est fausse. Je me suis fait un petit programme de test compilé avec GCC, du genre :
                #ifdef  __STDC_IEC_559__
                   puts("   __STDC_IEC_559__  defined!");
                #else
                   puts("   __STDC_IEC_559__  NOT defined!");
                #endif
                
                et c'est la 2è ligne qui a été exécutée. Pourtant j'ai bien l'impression que, comme tu l'as dit, ce sont les formats IEEE 754 qui sont utilisés (j'ai écrit des fonctions pour afficher la représentation en mémoire de flottants), mais après il faut dire que je n'ai pas beaucoup testé (je n'ai pas essayé les NaNs, infinis...).

                Connaîtriez-vous un moyen de savoir à coup sûr le format implémenté (ou du moins de savoir si c'est IEEE 754 ou pas) ?


                En fait, si je demande tout ça, c'est parce que je rédige actuellement un tutoriel sur les nombres flottants en C. Il y aura une sous-partie consacrée à la norme, que j'ai écrit en m'inspirant de ce topic. Je voudrais vous prévenir que j'ai parfois repris presque mot pour mot certaines de vos paroles, ça ne vous dérange pas ? Je pourrais vous citer en conclusion si vous le souhaitez.
                Je le mettrais bientôt en bêta-test (vous seriez gentils si vous pouviez lire la partie sur les normes pour me dire si j'ai écrit des monstruosités ^^ ).
                • Partager sur Facebook
                • Partager sur Twitter
                  30 juin 2011 à 16:47:01

                  Citation : Maelan44


                  Elle doit valoir 1 ou juste être définie ?



                  Elle doit être définie et valoir 1 ;)

                  Citation : Norme C99 6.10.8 § 1 p 160


                  _ _STDC_IEC_559_ _ The integer constant 1, intended to indicate conformance to the specifications in annex F (IEC 60559 floating-point arithmetic).



                  Sinon, chez moi cela ne pose pas de problème, la macroconstante est bien définie. Tu es certains de compiler en C99? Car cette macroconstante n'existe que depuis le C99.
                  • Partager sur Facebook
                  • Partager sur Twitter
                    30 juin 2011 à 17:06:02

                    Citation : Taurre

                    Tu es certain de compiler en C99 ? Car cette macroconstante n'existe que depuis le C99.


                    À peu près, oui : gcc -W -Wall -Wextra -pedantic -std=c99.

                    ÉDIT: J'ai oublié de dire que je compile sous Windows (si jamais ça a une incidence sur le comportement de GCC).

                    RÉ-ÉDIT: : En fait, j'ai des doutes. Je ne comprenais pas pourquoi, mais impossible d'afficher 64 bits avec printf("%llu",n), ce sont les 2 L qui provoquent des warnings, alors que c'est du C99 valide. À la réflexion je me demande si GCC n'y serait pas pour quelque chose. J'ai une ancienne version (3.4.5) et peut-être (?) qu'elle ne supporte pas pleinement le C99. Je vais essayer avec la version 4.4.0 que j'ai sous la main (mais qui est aussi un peu datée).
                    • Partager sur Facebook
                    • Partager sur Twitter
                      30 juin 2011 à 19:07:44

                      Je viens de tomber sur ce sujet, et plus précisément sur cette page. On peut y lire noir sur blanc:

                      Citation : GCC C99 status


                      IEC 60559 is IEEE 754 floating point. This works if and only if the hardware is perfectly compliant, but GCC does not define __STDC_IEC_559__ or implement the associated standard pragmas; nor do some options such as -frounding-math to enable the pragmas globally work in all cases (for example, required exceptions may not be generated) and contracting expressions (e.g., using fused multiply-add) is not restricted to source-language expressions as required by C99.



                      visiblement, GCC ne défini pas la macroconstante __STDC_IEC_559__, mais laisse cette définition à la bibliothèque C du système. Or, comme celle de Windows ne supporte pas le C99... :-°

                      De plus, un système peut visiblement encoder les nombres flottants suivant le format défini par la norme IEEE 754, sans pour autant remplir toutes les conditions de l'annexe F de la norme C99 (cf ce sujet).
                      • Partager sur Facebook
                      • Partager sur Twitter
                        30 juin 2011 à 19:53:52

                        Citation : Taurre

                        visiblement, GCC ne défini pas la macroconstante __STDC_IEC_559__, mais laisse cette définition à la bibliothèque C du système. Or, comme celle de Windows ne supporte pas le C99... :-°

                        D'accord, merci beaucoup. :) (j'étais déjà tombé sur ce sujet)

                        Citation : Taurre

                        De plus, un système peut visiblement encoder les nombres flottants suivant le format défini par la norme IEEE 754, sans pour autant remplir toutes les conditions de l'annexe F de la norme C99 (cf ce sujet).


                        Aille aille aille, ça complique tout.
                        Enfin au niveau de mon tutoriel ça ne devrait pas poser de problèmes car je ne détaille pas toutes les histoires d'environnement (<fenv.h>) et compagnie, je reste dans le basique (le format des nombres flottants en mémoire) ; j'imagine que dans le support partiel il y a quand même au moins le type float qui correspond au simple précision de IEEE 754 et double au double précision, sinon on ne peut plus du tout parler de support même "partiel".
                        Le seul problème c'est pour savoir si l'on est véritablement avec une implémentation basée sur IEEE 754. Au pire je dirais au lecteur de se renseigner pour son compilo, et/ou d'effectuer une série de tests en affichant la représentation en mémoire de nombres flottants pour voir si ça colle avec IEEE. Ce n'est pas infaillible, mais ça permettrait d'éliminer certains formats non-IEEE.

                        Vois-tu une objection à ce que je cite carrément ton message dans le tuto ? Ce serait le plus pratique et le plus honnête.
                        • Partager sur Facebook
                        • Partager sur Twitter
                          30 juin 2011 à 21:39:20

                          Citation : Maelan44


                          Le seul problème c'est pour savoir si l'on est véritablement avec une implémentation basée sur IEEE 754. Au pire je dirais au lecteur de se renseigner pour son compilo, et/ou d'effectuer une série de tests en affichant la représentation en mémoire de nombres flottants pour voir si ça colle avec IEEE. Ce n'est pas infaillible, mais ça permettrait d'éliminer certains formats non-IEEE.



                          Oui, il devrait y avoir moyen de vérifier à l'aide d'un code de ce type:


                          #include <stdio.h>
                          #include <stdlib.h>
                          #include <string.h>
                          
                          
                          int
                          main (void)
                          {
                             double n = 1;
                             long long m = 0x3FF0000000000000; /* 1 au format IEEE 754 */
                          
                             if (memcmp (&n, &m, sizeof n) == 0) {
                                puts ("Format IEEE 754 supporte");
                             }
                          
                             return EXIT_SUCCESS;
                          }
                          




                          Citation : Maelan44


                          Vois-tu une objection à ce que je cite carrément ton message dans le tuto ? Ce serait le plus pratique et le plus honnête.



                          Aucun problème pour moi ;)
                          Vérifie quand même si je n'ai pas dit de bêtises :-°
                          • Partager sur Facebook
                          • Partager sur Twitter
                            30 juin 2011 à 22:15:31

                            Merci pour la suggestion de code, je n'y avais pas pensé. :)

                            La limitation à tout ce genre de test est qu'on ne peut pas vérifier si par exemple les infinis et NaNs sont bien pris en charge selon IEEE 754 (j'en ai marre d'écrire ce n°).
                            J'ai peut-être parlé trop vite.
                            1.0 / +0.0 doit renvoyer +infini, il me semble, et de même on peut obtenir -infini ou NaN (0.0 / +0.0).

                            Je vais y travailler.
                            Le tuto sera probablement en bêta-test demain.
                            • Partager sur Facebook
                            • Partager sur Twitter
                              30 juin 2011 à 23:37:35

                              Citation : Maelan44

                              RÉ-ÉDIT: : En fait, j'ai des doutes. Je ne comprenais pas pourquoi, mais impossible d'afficher 64 bits avec printf("%llu",n), ce sont les 2 L qui provoquent des warnings, alors que c'est du C99 valide. À la réflexion je me demande si GCC n'y serait pas pour quelque chose. J'ai une ancienne version (3.4.5) et peut-être (?) qu'elle ne supporte pas pleinement le C99. Je vais essayer avec la version 4.4.0 que j'ai sous la main (mais qui est aussi un peu datée).


                              Fais gaffe, car si tu es sous Windows, ça peut poser des problèmes avec printf(). Je sais que le printf() de msvcrt (le runtime C de Microsoft utilisé par MinGW pour une partie des fonctions C, et qui date de 1998(!), c'est dire s'il respecte les standards... :-° ) pose par exemple des problèmes avec long long signed/unsigned (le format standard ne fonctionne pas, il faut utiliser %I64 ou %I64u). Pour les flottants, je n'en sais rien, mais je n'ose même pas imaginer...
                              • Partager sur Facebook
                              • Partager sur Twitter
                                1 juillet 2011 à 11:46:18

                                Citation : yoch

                                Fais gaffe, car si tu es sous Windows, ça peut poser des problèmes avec printf(). Je sais que le printf() de msvcrt (le runtime C de Microsoft utilisé par MinGW pour une partie des fonctions C, et qui date de 1998(!), c'est dire s'il respecte les standards... :-° ) pose par exemple des problèmes avec long long signed/unsigned (le format standard ne fonctionne pas, il faut utiliser %I64 ou %I64u). Pour les flottants, je n'en sais rien, mais je n'ose même pas imaginer...


                                Pour les besoins de mes tests, j'avais finalement écrit vite fait ma propre fonction pour afficher 64 bits (entiers), avec une structure de 2 uint32_t : en prenant l'adresse de la variable, en convertissant sur un pointeur sur structure et en affichant dans le bon ordre chacun des 2 membres (solution crade et non portable à cause de l'endianness mais bon j'avais pas le temps ni le besoin de concevoir un bon petit algo).
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  2 juillet 2011 à 16:52:29

                                  Mon tutoriel est en bêta-test. :)
                                  Il est disponible ici.

                                  Pourriez-vous relire la dernière partie (c'est celle qui nous concerne) et dire ce que vous en pensez, voire relever les bêtises ?



                                  Par ailleurs, concernant le code qui permet de savoir si on utilise IEEE 754, finalement je me suis débrouillé sans memcmp.
                                  Mais les tests que j'ai choisi ne sont peut-être pas les plus judicieux (d'autant plus que ce n'est sans doute pas utile de tester le positif et le négatif à chaque fois ?), ou pas suffisant (effectuer une série de tests basés sur des nombres aléatoires ?).


                                  Mais je me demandais si on ne pouvais pas tout simplement se contenter de tester ces constantes de préprocesseur (cf les pages 2 et 3) fournies pas <float.h> ?

                                  Il faut que je me penche sur les autres formats possibles, mais apparemment la 1ère page du lien précédent semble insinuer qu'il s'agit toujours de la chanson "signe-exposant-mantisse", avec variations de taille des différents champs, présence ou absence de bit implicite...
                                  Si c'est la cas, le test de ces macros suffirait à déterminer le format à l'exclusion de tout autre. >_<
                                  Et cela permettrait en plus de déterminer tout cela à l'étape de compilation et non d'exécution, on ne compilerait ainsi pas de code inutile.
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    3 juillet 2011 à 20:37:48

                                    Salut,

                                    J'ai déjà répondu, mais je reprends en substance :

                                    Selon moi, l’intérêt de connaitre l'implémentation des nombres flottants sur sa machine/son compilo est faible, car pour la machine, ça ne doit pas être bien difficile à vérifier, et inutile de le faire de façon logicielle (une architecture processeur est clairement déterminée, ce n'est pas comme les cartes graphiques ou autres), et pour le compilo : 1. il y a une macro faite pour ça, et 2. ton code ne permets pas de connaitre si le compilo est entièrement conforme à la norme, on vérifie simplement le représentation mémoire, donc l’intérêt reste assez limité.

                                    Pour le test des macros, je n'en sais trop rien, je ne connais pas du tout les différentes représentations des flottants.

                                    Pour ce qui est du code lui même, je ne me suis pas penché dessus.
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      3 juillet 2011 à 21:08:54

                                      Merci de ta réponse :) , mais finalement je pense qu'il vaut mieux continuer cette discussion sur le topic de la bêta (on sort du sujet initial de ce topic-ci). C'est ma faute, c'est moi qui t'ai redirigé ici.
                                      • Partager sur Facebook
                                      • Partager sur Twitter

                                      Les nombres à virgule flottante du point de vue de la norme

                                      × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                      × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                      • Editeur
                                      • Markdown