URL: https://linuxfr.org/news/cpp17-exprime-la-virgule-flottante-en-hexadecimal-et-offre-des-cadeaux-aux-lecteurs-de-linuxfr-org Title: C++17 exprime la virgule flottante en hexadécimal et offre des cadeaux aux lecteurs de LinuxFr.org Authors: Adrien Jeser Davy Defaud, Oliver, ZeroHeure et Storm Date: 2016年12月02日T20:45:29+01:00 License: CC By-SA Tags: c++17, c++ et humour Score: 30 Chaque jour (ouvré) de décembre a droit à sa surprise. Après le [`if constexpr`](http://linuxfr.org/news/cpp17-branche-a-la-compilation-if-constexpr), aujourd’hui, le calendrier de l’Avent du C++ présente la spécification technique [P0245](http://wg21.link/p0245) concernant le littéral pour exprimer la virgule flottante en hexadécimal. [![Les Nerdettes s’entraînent pour le concours des littéraux hexadécimaux sur LinuxFr.org](https://cpp-frug.github.io/materials/images/nerdettes_litteral_hexa.svg)](https://github.com/cpp-frug/materials/blob/gh-pages/images/nerdettes_litteral_hexa.svg) ---- [1er décembre : « C++17 fixe l’ordre d’évaluation des expressions »](http://linuxfr.org/news/cpp17-fixe-l-ordre-d-evaluation-des-expressions) [2 décembre : « C++17 indique la disponibilité des en‐têtes »](http://linuxfr.org/news/cpp17-indique-la-disponibilite-des-entetes-header) [5 décembre : « C++17 branche à la compilation »](http://linuxfr.org/news/cpp17-branche-a-la-compilation-if-constexpr) [Cette dépêche sur le dépôt Git du C++FRUG](https://github.com/cpp-frug/materials/blob/gh-pages/news/2016-12-06_cpp17-exprime-la-virgule-flottante-en-hexadecimal.md) ---- Spécification technique ======================= La réunion de Jacksonville en février 2016 a amendé le *TS* [P0245](http://wg21.link/p0245) qui permet d’exprimer les [virgules flottantes (IEEE 754)](https://fr.wikipedia.org/wiki/Virgule_flottante#Norme_IEEE_754) en hexadécimal. Le C++ permet enfin d’avoir une représentation exacte des [virgules flottantes](http://en.cppreference.com/w/cpp/language/floating_literal). Cette fonctionnalité était déjà présente depuis longtemps dans d’autres langages : C99, Java 5 (2004)... Représentation exacte ===================== La représentation hexadécimale a l’avantage d’être celle du registre (mémoire binaire). Attention à la notation décimale des virgules flottantes. Par exemple, `0.1f` ne vaut pas exactement `0.1` mais `0.10000000149...`. Un [exemple](http://coliru.stacked-crooked.com/a/7b70c88142f28581) : ```cpp #include // int64_t #include // std::cout int main() { float un_dixieme = 0.1f; float f_1e11 = un_dixieme * 1e12f; // Erreur d'arrondi int64_t i_1e11 = 0.1 * 1e12; // Pas d'erreur d'arrondi double diff = f_1e11; diff -= i_1e11; // soustraction f_1e12 - i_1e11 std::cout.precision(99); std::cout << "un_dixieme = "<< un_dixieme << "\n" "f_1e12 = "<< f_1e11 << "\n" "i_1e12 = "<< i_1e11 << "\n" "diff = "<< diff << '\n'; } ``` Qui donne le résultat : ```cpp un_dixieme = 0.100000001490116119384765625 f_1e12 = 99999997952 i_1e12 = 100000000000 diff = -2048 ``` Les hexadécimaux permettent d’écrire la représentation exacte des virgules flottantes en s’affranchissant de ces erreurs d’arrondis. La pratique =========== Passons à la pratique des fractions hexadécimales : ```cpp float v = 0xa.bp3f; assert(v == 85.5f); ``` * `0xA.B = 0xA*16^0 + 0xB*16^-1` * `0xA = 10` * `0x.B = 11/16 = 0,6875` * `0xA.B = 10,6875` * `p3 = 2^3 = 8` * `v = 10,6875*8 = 85,5` * `'f' final = type 'float'` ```cpp double w = 0xC0DE2017.1CAFEp-1; assert(w == 1617891339.55602931976318359375); ``` * `0xC0DE2017 = 3235782679` * `0x1CAFE = 117502` * `0xFFFFF = 1048576` * `p-1 = 2^-1 = 1/2` * `w = (3235782679 + 117502/1048576) / 2` Concours ======== Chère lectrice, cher lecteur *LinuxFr.org*, as‐tu d’autres idées de jeux de mots avec cette notation hexadécimale ? Alors, défoule‐toi dans les commentaires. ;-) Les plus beaux jeux de mots seront récompensés avec des cadeaux de la part de *LinuxFr.org*. Alors, tu as un peu de temps disponible aujourd’hui ? Fais comme les *Nerdettes* (les deux filles sur l’illustration), bogue du cerveau et propose ton code source de folie pour tenter de remporter un des lots. Les modalités : * les jeux de mots peuvent utiliser le nom des variables, la forme des caractères et symboles, les opérateurs ; * il faut au moins un littéral hexadécimal à virgule flottante ; * le code source doit pouvoir être compilable par un compilateur C++17 ; * le code source doit être sous licence libre (licence de ton choix) ; * les meilleures réponses seront sélectionnées parmi celles qui auront le plus de points « *pertinents* » et le moins de points « *inutiles* » ; * la liste des gagnants sera diffusée quelques jours plus tard, dans une autre dépêche C++ ; * les vainqueurs auront un livre à choisir parmi ceux des éditions Eyrolles et ENI ; * réception des récompenses par courrier ~~électronique~~ postal. Notation ======== Remarquez le `p` à la fin. Celui‐ci représente l’exposant binaire et il est suivi par un entier décimal (et non pas hexadécimal). Cet exposant binaire est obligatoire pour plusieurs raisons : * il évite l’ambiguïté du `f` final dans `0xA.Bf` (`float` ou le chiffre `F` hexadécimal ?) ; * il évite l’ambiguïté du `E` dans `0xa.bE-12` (exposant `-12` ou `0xA.BE - 12` ?) ; * il correspond à la norme [IEEE 754](https://fr.wikipedia.org/wiki/IEEE_754) (puissance de deux) ; * 100 % compatible avec la notation C99 (et celle d’autres langages). Tentons de représenter cette notation hexadécimale en [expression rationnelle](https://fr.wikipedia.org/wiki/Expression_rationnelle) : * `0[xX][0-9a-fA-F]+[.]?[pP][+-]?[0-9]+[fFlL]?` ; * `0[xX][0-9a-fA-F]*[.][0-9a-fA-F]+[pP][+-]?[0-9]+[fFlL]?`. Termes du standard ================== Allez, soyons curieux, regardons comment le standard C++ spécifie cette notation avec un extrait du chapitre **§ 2.13.4 _Floating literals_** du [brouillon C++17](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf) :> *hexadecimal-floating-literal:*>   *hexadecimal-prefix hexadecimal-fractional-constant binary-exponent-part floating-suffixopt*>   *hexadecimal-prefix hexadecimal-digit-sequence binary-exponent-part floating-suffixopt*> *hexadecimal-fractional-constant:*>   *hexadecimal-digit-sequenceopt . hexadecimal-digit-sequence*>   *hexadecimal-digit-sequence .*> *binary-exponent-part:*>   `p` *signopt digit-sequence*>   `P` *signopt digit-sequence*> *sign:* one of>   `+` `-`> *digit-sequence:*>   *digit*>   *digit-sequence ’opt digit*> *floating-suffix:* one of>   `f` `l` `F` `L` Et l’équivalent chez [_cppreference.com_](http://en.cppreference.com/w/cpp/language/floating_literal) : > `0x | 0X hex-digit-sequence`> `0x | 0X hex-digit-sequence .`> `0x | 0X hex-digit-sequence(optional) . hex-digit-sequence`>> _Hexadecimal digit-sequence representing a whole number without a radix separator. The exponent is never optional for hexadecimal floating-point literals:_ `0x1ffp10`, `0X0p-1`, `0x1.p0`, `0xf.p-1`, `0x0.123p-1`, `0xa.bp10l`>> _The exponent syntax for hexadecimal floating-point literal has the form:_> `p | P exponent-sign(optional) digit-sequence`>> _exponent-sign, if present, is either `+` or `-`_>> _suffix, if present, is one of `f`, `F`, `l`, or `L`. The suffix determines the type of the floating-point literal:_>> * _(no suffix) defines double_> * _`f F` defines float_> * _`l L` defines long double_ Utilisation de `strtof()` et `std::hexfloat` ============================================ En attendant C++17, il est possible d’utiliser [`strtof()`](http://en.cppreference.com/w/cpp/string/byte/strtof) et [`std::hexfloat`](http://en.cppreference.com/w/cpp/io/manip/fixed) pour jouer avec les virgules flottantes hexadécimales : ```cpp #include #include #include int main (int argc, char *argv[]) { if (argc != 2) { std::cout <<"usage: "<< argv[0] <<" 0xA.Bp-1 => Decode hexfloat" "\n"; return 1; } std::cout <<"decode floating point hexadecimal = "<< argv[1]; long double l = std::strtold(argv[1],NULL); if(errno==ERANGE)std::cout<<"\nstrtold() erreur"; double d = std::strtod (argv[1],NULL); if(errno==ERANGE)std::cout<<"\nstrtod() erreur"; float f = std::strtof (argv[1],NULL); if(errno==ERANGE)std::cout<<"\nstrtod() erreur"; std::cout <<"\n" "long double = "<< std::defaultfloat << l <<'\t'<< std::hexfloat << l <<"\n" "double = "<< std::defaultfloat << d <<'\t'<< std::hexfloat << d <<"\n" "float = "<< std::defaultfloat << f <<'\t'<< std::hexfloat << f <<'\n'; } ``` Nous pouvons regretter qu’il faille utiliser des fonctions `strtof()` issues du C, qui imposent de vérifier si `errno == ERANGE`. En théorie, `std::hexfloat` devrait fonctionner pour l’entrée (`istream`). Mais dans la pratique `std::hexfloat` semble ne fonctionner que pour la sortie (`ostream`). L’exemple suivant ne fonctionne toujours pas avec GCC 6.2, Clang 3.9 et MSVC++15 : ```cpp double d; std::istringstream iss("0xA.Bp-1"); iss>> std::hexfloat>> d; std::cout << d; ``` Notons que c’est l’extraction qui ne s’effectue pas correctement. Le `std::istringstream` reste quand à lui dans un état correct, ainsi les erreurs sont vérifiables : ```cpp std::cout << std::boolalpha << iss.fail() << '\n' // false << iss.bad() << '\n' // false << iss.eof() << '\n' // false << iss.str() << '\n';// "0xA.Bp-1" ``` Réutilisation ============= Le texte de cette dépêche est protégé par ~~le [droit d’auteur](https://fr.wikipedia.org/wiki/Droit_d%27auteur#Droit_d.E2.80.99auteur_traditionnel_vs._licences_de_libre_diffusion)~~ la [gauche d’auteur](https://fr.wikipedia.org/wiki/Gauche_d'auteur) et réutilisable sous licence [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.fr). Les images utilisées sont aussi sous licence libre (cliquer sur l’image pour plus de détails). Donc, n’hésitez pas à réutiliser ce contenu libre pour créer, par exemple, des supports de formation, des présentations (_Meetups_), des publications sur d’autres blogs, des articles pour des magazines, et aussi un article C++17 sur Wikipédia dès que [Wikipédia passera de la licence CC BY-SA 3.0 à la CC BY-SA 4.0](https://meta.wikimedia.org/wiki/Terms_of_use/Creative_Commons_4.0) (le contenu de cette dépêche utilise la version la CC BY-SA 4.0). Les auteurs =========== Par respect de la licence, merci de [créditer](https://fr.wiktionary.org/wiki/cr%C3%A9diter#Verbe) les auteurs : * les principaux auteurs sont [Adrien Jeser](https://linuxfr.org/users/jeser) et [Oliver H.](https://linuxfr.org/users/oliver_h) ; * les nombreux autres contributeurs ayant contribué sur l’ancêtre de cette dépêche ou sur le [dépôt Git](https://github.com/cpp-frug/materials) sont : [_eggman_](https://linuxfr.org/users/eggman), [Yves Bourguignon](https://linuxfr.org/users/biomin), [_Storm_](https://linuxfr.org/users/storm--2), [_gorbal_](https://linuxfr.org/users/gorbal), [_palm123_](https://linuxfr.org/users/palm123), [_khivapia_](https://linuxfr.org/users/khivapia), [_BAud_](https://linuxfr.org/users/baud), [_Segfault_](https://linuxfr.org/users/elly), [Benoît Sibaud](https://linuxfr.org/users/oumph), [Lucas](https://linuxfr.org/users/george), [_cracky_](https://linuxfr.org/users/cracky), [Martin Peres](https://linuxfr.org/users/mupuf), [_RyDroid_](https://linuxfr.org/users/rydroid), [_olibre_](https://github.com/olibre) et [_Guss_](https://github.com/GuillaumeDua). Continuer à améliorer ce document ================================= Malgré tout le soin apporté, il reste certainement des oublis, des ambiguïtés, des fôtes... Bien que cette dépêche restera figée sur le site *LinuxFr.org*, il est possible de continuer à l’enrichir sur le [dépôt Git](https://github.com/cpp-frug/materials/blob/gh-pages/news/2016_n3_Cpp17_Nouveautes-du-langage.md) du [**Groupe des utilisateurs C++ francophone**](http://linuxfr.org/news/douzieme-rencontre-parisienne-c-mercredi-27-avril-2016#historique-des-rencontres-c-francophones) (C++FRUG). C’est donc sur ce dépôt que se trouvent les versions les plus à jour.   **(ღ ̆⌣ ̆ღ)** Alors que cet article restera figé sur le site *LinuxFr.org*, il continuera d’évoluer sur le dépôt Git. Merci de nous aider [à maintenir ce document à jour][md] avec vos questions/suggestions/corrections. L’idée est de partager ce contenu libre et de créer/enrichir des articles Wikipédia quand la licence [sera CC BY-SA 4.0](https://meta.wikimedia.org/wiki/Terms_of_use/Creative_Commons_4.0/fr).   **٩(•‿•)۶** Appel à contribution ==================== Nous nous efforçons de vous fournir une dépêche de qualité chaque jour ouvré. Et, en plus, avec une illustration sympathique. Mais cela demande beaucoup de travail, et tenir les délais n’est pas toujours simple. Merci de nous donner un coup de main, que ce soit sur la correction orthographique, le dessin, la lecture des spécifications techniques, la rédaction d’une nouvelle dépêche à intégrer au calendrier de l’Avent du C++. Tu peux aussi apporter ta contribution à la dépêche [_Faut‐il continuer à apprendre le C++ ?_](https://linuxfr.org/news/faut-il-continuer-a-apprendre-le-c) Rejoins‐nous sur la [page du groupe de travail C++](https://linuxfr.org/redaction/news/groupe-de-travail-depeches-c-17) sur _LinuxFr.org_ (un [compte](https://linuxfr.org/compte/inscription) est nécessaire pour y accéder). À suivre...

AltStyle によって変換されたページ (->オリジナル) /