Journal Le Pourquoi Windows plante !

Posté par  .
Étiquettes : aucune
0
18
août
2007
Voici un soucis que j'ai eu aujourd'hui et qui m'as fait perdre deux bonnes heures de boulot pour RIEN !

Bon, c'est en relation avec Microsoft Windows(c) mais ça prouve bien que même le B-A-BA du fonctionnement d'un logiciel et biaisé par l'OS, et provoque donc des plantages là où ça ne devrai pas.

Alors voici mon problème, je suis en cours de portage d'une de mes applications vers Windows(c)... :-(
Remarquant un bug d'une valeur, je part à la chasse au bug pour finalement isoler le bug suivant :
double a = 800.0 ;
double b = 0.75 ;
double c = 0.6 ;
double d = a*b*c ;
printf("Prend %f * %f * %f = %f ( %i ) = %f ( %i ) %\n", a, b, c, a*b*c, (int)(a*b*c), d, (int)d) ;


Même un développeur de base comprendra ce code.
Et voici le résultat de l'exécution sous Windows (aucun soucis sous linux) :
Prend 800.000000 * 0.750000 * 0.600000 = 360.000000 ( 359 ) = 360.000000 ( 360 )

Peut-t-on m'expliquer ce que fou là ce 359 !? Windows compte mal à ce point ?
Ca m'inquiète sincèrement sur la qualité d'application même bien développé...

Pour information, ça a été compilé avec g++ 3.4.4 sous Windows.


PS: A la base, je voulais poster ça dans le forum de developpez.net (qui est mieux pour les questions dev), mais il est HS. Donc j'ai voulu mettre sur ce forum, qui n'est pas accessible non plus, donc en désespoir de cause, je poste ici :-)
  • # Erreur de base...

    Posté par  (site Web personnel) . Évalué à 10.

    Il faut utiliser "choux" et non "double", parce que au moins on comprend ce que windows plante :)
    • [^] # Re: Erreur de base...

      Posté par  . Évalué à 3.

      huhu, ça m'a rappelé une vieille musique...

      http://www.sim07.net/2007/06/25/savez-vous-planter-windows-a(...)

      oui oui c'est vieux !
      • [^] # Re: Erreur de base...

        Posté par  (site Web personnel) . Évalué à 3.

        excellant.

        ça me rappel un concours sur "les meilleurs moyens de planter windows" parus dans un des premiers si ce n'est le premier virus mag.
        Un des vainqueurs avait simplement pris une pelle, creusé un trou et mis le windows dedans.
        • [^] # Re: Erreur de base...

          Posté par  (site Web personnel) . Évalué à 1.

          En fait, c'était des moyens sérieux, que le concours demandait. Sérieux et reproductibles à coup sûr. Pas un n'avait été retenu, et le magazine, je m'en souviens bien, avait été assez marri, et avait donc choisi de publier ce moyen allégorique et rigolo.
          • [^] # Re: Erreur de base...

            Posté par  (site Web personnel) . Évalué à 4.

            Y a pas besoin d'être en rade de vainqueurs pour récompenser ça. Juste d'humour.
          • [^] # Re: Erreur de base...

            Posté par  (site Web personnel) . Évalué à 2.

            reproductibles ?

            Le problème de certains bugs sous windows (de mémoire) est justement d'être imprévisibles.
          • [^] # Re: Erreur de base...

            Posté par  . Évalué à 2.

            Faux ! Dans mes souvenirs, c'était pour win95 et il y avait une solution qui marchait partout (et crois moi, j'ai essayé après sur pas mal de machines à l'époque)
            Et pourtant c'était tout con : un mot de passe du screen saver de plus de 20 caractères. (+ lancer le poste de travail pour l'achever...)

            Les 2 personnes ont été déclarées vainqueurs.
            • [^] # Re: Erreur de base...

              Posté par  . Évalué à 4.

              Démarrer->exécuter: "con/con"
              Pour win98 et 95 me semble ^^
              • [^] # Re: Erreur de base...

                Posté par  . Évalué à 1.

                ça marche pas chez moi (98).

                forcement je les ai mis à jour ...
                • [^] # Re: Erreur de base...

                  Posté par  . Évalué à 2.

                  étrange ca marchait super bien sur mon ancien 98 2nde édition...
                  (Faut pas mettre les " hein)
                  • [^] # Re: Erreur de base...

                    Posté par  . Évalué à 1.

                    guillemet ou pas c'est équivalent.

                    et sinon il suffisait juste de télécharger le patch correctif de chez microsoft, puisque ça permettait de faire des DOS sur des serveurs web ...
                    • [^] # Re: Erreur de base...

                      Posté par  . Évalué à 3.

                      Ouai mais même une fois patché j'avais trouvé un "équivalent" qui marchait encore sur 2k.
                      Avec la plupart des serveur FTP, si tu demandait un get comX suivi d'une coupure de la connexion (ctrl+c), bah tu avais soit un crash du serveur ftp soit un freeze du thread avec 100% de cpu à l'infinie.
                      Testé avec guildftp et autres comme serv-u (ca marchait y a deux ans environ, je sais pas si ça a été corrigé) par contre si tu faisait pas le ctrl+c ca tombait pas, je ne sais pas si y a le même genre de problème sous nulix mais c'était marrant et simple comme "DOS" ^^
                • [^] # Re: Erreur de base...

                  Posté par  (site Web personnel) . Évalué à 3.

                  Essaie:
                  c:\con\con\com1\null\con

                  Ca crash sur une machine mis à jour.
  • # OS ?

    Posté par  . Évalué à 4.

    C'est plutot le compilo qu'il faudrait regarder avant l'os ;)

    T'as essayé avec des versions plus récentes de g++ si possible ? Avec des options de compilations différentes ?

    Sinon des indications peut être avec g++ -s et le code assembleur généré, tu peux comparer ce que t'as sous windows et sous linux.

    Autre info qui manque, le processeur c'est quoi ? ( moi j'en ferai rien, mais ca peut peut être jouer ... )
    • [^] # Re: OS ?

      Posté par  . Évalué à 4.

      Autre piste, genre c'est peut être un arrondi qui tourne mal, un cast ça te tronque le nombre, ça l'arrondis pas.
      Mais c'est peu probable, genre tu fais que des calculs avec des entiers, tout devrait être représentable sans trop de soucis sur des double.


      http://www.codecogs.com/reference/math.h/round.php?alias=rou(...)
    • [^] # Re: OS ?

      Posté par  . Évalué à 5.

      >Avec des options de compilations différentes ?

      Cachez ce -O que je ne saurais voir.
  • # ...

    Posté par  . Évalué à 3.

    Marrant, ça. T'as essayé avec un autre g++, voire un autre compilo ? À mon avis, l'erreur vient plutôt de là, mais il y a longtemps que je n'ai plus fait de C/C++.
  • # Chez moi...

    Posté par  . Évalué à 10.

    steve@myhost ~ $ cat test.c
    #include <stdio.h>

    int main() {
    double a = 800.0 ;
    double b = 0.75 ;
    double c = 0.6 ;
    double d = a*b*c ;
    printf("Prend %f * %f * %f = %f ( %i ) = %f ( %i ) %\n",
    a, b, c, a*b*c, (int)(a*b*c), d, (int)d) ;
    return 0;
    }
    steve@myhost ~ $ gcc test.c -o test
    steve@myhost ~ $ ./test
    Prend 800.000000 * 0.750000 * 0.600000 = 360.000000 ( 359 ) = 360.000000 ( 360 ) %
    steve@myhost ~ $ llvm-gcc test.c -o test
    steve@myhost ~ $ ./test
    Prend 800.000000 * 0.750000 * 0.600000 = 360.000000 ( 360 ) = 360.000000 ( 360 ) %
    steve@myhost ~ $ icc test.c -o test
    steve@myhost ~ $ ./test
    Prend 800.000000 * 0.750000 * 0.600000 = 360.000000 ( 360 ) = 360.000000 ( 360 ) %
    steve@myhost ~ $ gcc --version
    gcc (GCC) 4.2.1
    Copyright © 2007 Free Software Foundation, Inc.
    Ce logiciel est libre; voir les sources pour les conditions de copie. Il n'y a PAS
    GARANTIE; ni implicite pour le MARCHANDAGE ou pour un BUT PARTICULIER.

    steve@myhost ~ $ llvm-gcc --version # LLVM 2.0
    llvm-gcc (GCC) 4.0.1 (Apple Computer, Inc. build 5449)
    Copyright © 2005 Free Software Foundation, Inc.
    Ce logiciel est libre; voir les sources pour les conditions de copie. Il n'y a PAS
    GARANTIE; ni implicite pour le MARCHANDAGE ou pour un BUT PARTICULIER.

    steve@myhost ~ $ icc --version
    icc (ICC) 10.0 20070426
    Copyright (C) 1985-2007 Intel Corporation. All rights reserved.
    • [^] # Re: Chez moi...

      Posté par  . Évalué à 4.

      Je confirme, je viens d'essayer avec gcc 4.1.2 (Ubuntu 4.1.2-0ubuntu4), et j'obtiens la valeur erronée de 359.
      • [^] # Re: Chez moi...

        Posté par  (site Web personnel) . Évalué à 3.


        $ tcc -run test.c
        Prend 800.000000 * 0.750000 * 0.600000 = 360.000000 ( 359 ) = 360.000000 ( 360 ) %
        $ tcc -v
        tcc version 0.9.23


        Même chose avec gcc sous Solaris et Windows.
        Je cherche d'autre compilateur pour tester.
    • [^] # Re: Chez moi...

      Posté par  . Évalué à 2.

      chez moi tout marche parfaitement bien si je met un -O ...
    • [^] # Re: Chez moi...

      Posté par  . Évalué à 3.

      Pareil ici, mais:
      /tmp 19:04 gcc -ffloat-store test.c -o test && ./test
      Prend 800.000000 * 0.750000 * 0.600000 = 360.000000 ( 360 ) = 360.000000 ( 360 ) %

      Ça marche donc avec -ffloat-store qui est activé pour -O1 (ou supérieur) ou -Os. En fait, il n'y a que -O0 qui fout la merde :)
      • [^] # Re: Chez moi...

        Posté par  (site Web personnel) . Évalué à 1.

        pas du tout, -ffloat-store n'est pas vraiment une option "d'optimisation", ça a plutôt tendance à ralentir le code. Et elle n'est pas automatiquement activée par -O1 et supérieur.

        Le but de cette option est d'empêcher de se trimballer des résultats intermédaires flottants ayant plus de 64 bits de précision. Donc ça rajoute (en gros) des store/load après chaque calcul flottant pour forcer une troncation à 64 bits et assurer un calcul plus conforme à la norme IEEE.

        Le meilleur moyen d'éviter ces problèmes, c'est d'utiliser une plateforme faisant ses calculs flottans en 64bits, donc abandonner le FPU x87. C'est en fait trés facile avec un ordi récent, il suffit d'utiliser le SSE2: option -msse2 -mfpmath=sse pour un x86, rien à faire pour un x86_64.
  • # c'est normal

    Posté par  . Évalué à 10.

    bienvenue au monde des nombres flottants et de leur représentation approchée

    le cast d'un flottant vers un int consiste à tronquer la partie fractionnaire (donc arrondi vers 0).

    dans un cas tu tronques un double (d) dont la représentation (64 bits IEEE754) est visiblement >= 360 et < 361 (et donc tu récupères 360)
    dans l'autre tu tronques le résultat d'une opération de multiplication entre doubles dont la structure interne peut (et visiblement c'est le cas) être plus précise qu'un double (parfois c'est 80bits, parfois 128, etc.) et qui dépend du compilo, de ta plateforme et de l'age du capitaine. Dans cette représentation temporaire, (a*b*c) est visiblement >= 359 et < 360.

    Essaie donc de faire : (int)((double)(a*b*c)) et je suppute un petit 360.

    Autre solution plus simple, lorsqu'on veut arrondir (mais je ne m'avance pas trop ce n'est pas forcément ce que tu veux faire ici) on fait plutôt (int)(x + 0.5) pour x >= 0

    Je t'invite à (re)lire "What Every Computer Scientist Should Know About Floating-Point Arithmetic" : http://www.physics.ohio-state.edu/~dws/grouplinks/floating_p(...)
    • [^] # Re: c'est normal

      Posté par  . Évalué à 9.

      Encore plus simple : utiliser les fonctions standards telles que floor(), round(), ceil() pour faire les arrondis de nombre à décimale à priori puis caster le résultat en int enfin. En gros, ce qu'on apprend en classe CP de C ;)

      « Je vous présente les moines Shaolin : ils recherchent la Tranquillité de l'Esprit et la Paix de l'Âme à travers le Meurtre à Main Nue »

    • [^] # Re: c'est normal

      Posté par  (site Web personnel) . Évalué à 1.

      Essaie donc de faire : (int)((double)(a*b*c)) et je suppute un petit 360.

      Bof, le (a*b*c) est déjà de type double, le cast supplémentaire vers double ne sert à rien et sera sans doute ignoré par le compilo.

      Ca dépend vraiment du backend du compilo, c'est difficile d'influer là dessus en modifiant le source C. Plein de paramètres peuvent influencer l'assembleur généré: le niveau d'optim, le code environnant, etc.
  • # "Facile"...

    Posté par  . Évalué à 6.

    Et apres, on me repetera que les cours d'informatique de fac ne servent a rien... Vu en L2 (ex-DEUG2). Vu qu'on utilise une troncature en castant c'est normal. J'ai fait les calculs vite fait en IEEE-754 64 bits et effectivement ca donne un chouilla moins que 360.0 (c'est lourd, je vais pas les recopier).
    Merci a Bernard Parisse, UJF.
    • [^] # Re: "Facile"...

      Posté par  . Évalué à 3.

      Sauf que là, on est en train de lui dire que le bug qu'il a trouvé n'en est pas un, et que celui qu'il cherche en est un autre :)
  • # grumph

    Posté par  (site Web personnel) . Évalué à 9.

    Il y a encore des développeurs C à qui on n'a pas appris que le travail sur les flottant c'est de l'approximation, avec tous les problèmes que ça pose quand on fait une troncature ou un test d'égalité ?

    Quand je vois ça en PHP ou dans les scripts j'accepte que le développeur ne sache pas, mais quand on travaille à bas niveau comme dans le C, ça fait partie des pré-requis je pense.

    Le pire c'est quand le développeur ne peut penser que à un bug des outils, jamais à se remettre en cause.
    • [^] # Re: grumph

      Posté par  . Évalué à 10.

      pour lui, c'est même pas un bug de l'outil, mais un bug de l'OS...

      en fait il s'est trompé de jour, il aurait dû poster hier... ;-)
    • [^] # Re: grumph

      Posté par  . Évalué à 2.

      Ou pire, blâmer le mauvaise outil...
    • [^] # Re: grumph

      Posté par  . Évalué à 8.

      Le pire c'est quand le développeur ne peut penser que à un bug des outils, jamais à se remettre en cause.
      Les libristes* ont une réputation à maintenir, celle de critiquer microsoft dès que possible, même si la raison n'est pas bonne. Du coup, ce journal se justifie. Non ?

      * je fais une généralité, même si c'est une minorité, cette minorité suffit à faire la réputation de tous, un peu comme "les supporters de foots sont idiots"
      • [^] # Re: grumph

        Posté par  (site Web personnel) . Évalué à 10.

        * je fais une généralité, même si c'est une minorité, cette minorité suffit à faire la réputation de tous, un peu comme "les supporters de foots sont idiots"

        Alors la je ne suis pas d'accord, si c'était le même cas, on dirait que les supporter de foot sont intelligents puisque c'est la que ce trouve la minorité.
  • # géo trouvetout

    Posté par  (site Web personnel) . Évalué à 6.

    bon, on a donné des réponses farfelues d'informatichiens plus haut, je ne m'éttendrai pas, mais c'est du niveau cm1, dans mon picsou, je m'en souviens très bien, géo trouvetout avait inventé une calculatrice super précise où 2+2=3.9999999999999 l'équipe de gcc a sûrement encore bafoué un brevet
    • [^] # Re: géo trouvetout

      Posté par  . Évalué à 8.

      En fait l'approximation ne porte pas sur des flottants qui n'ont pas de décimale. Par contre 1.30+2.70 ça fait bien 3.99999
      L'explication c'est qu'en binaire 1.30 ça fait à peu près 1.0100110011001100110011001100110011001100110011001101 (c'est une approximation) et 2.70 ça fait 10.10110011001100110011001100110011001100110011001101 (également un approximation).
      Et quand tu additionne les deux (1.0100110011001100110011001100110011001100110011001101+
      10.10110011001100110011001100110011001100110011001101 un ordinateur ça calcule en binaire) ça donne 11.1111111111111111111111111 soit 3.999999999999999.
      Tu vois que le calcul est donc rigoureusement exact :)

      Pour vérifier tu trouveras un convertisseur ici : http://www.michelcarrare.com/demos/converter.php

      @+
      Calvin
      • [^] # Re: géo trouvetout

        Posté par  (site Web personnel) . Évalué à 5.

        Comment tu te la pètes !

        Heureusement que je t'avais expliqué ça vendredi sinon ça aurait pu être toi l'auteur de ce message. Surtout que toi aussi tu n'aimes pas trop Microsoft : "Word c'est de la merde" :-)

        L'association LinuxFr ne saurait être tenue responsable des propos légalement repréhensibles ou faisant allusion à l'évêque de Rome, au chef de l'Église catholique romaine ou au chef temporel de l'État du Vatican et se trouvant dans ce commentaire

        • [^] # Re: géo trouvetout

          Posté par  . Évalué à 3.

          Oui je dois reconnaitre que c'était plutôt bien tombé.
          Si tu ne me l'avais pas expliqué j'aurai dit "moi aussi moi aussi j'ai des bugs comme ça en java ! Windows et Java c'est buggé".
          Alors qu'en fait non, enfin en tout cas pas Java.
          Merci de m'avoir permis de briller en société, j'en ai besoin...
  • # 2+2=5

    Posté par  . Évalué à 3.

    for extremely large values of 2

    http://www.thinkgeek.com/tshirts/coder/60f5/
    • [^] # Re: 2+2=5

      Posté par  (site Web personnel) . Évalué à 3.

      Tiens ça me fait penser à un petit test en Forth :

      variable two 3 two !
      : 2 ( -- n) 5 two @ - dup two ! ;

      Qui permet de calculer 2+2=5
      2 2 + . -> 5 :-)
      2 . -> 2
      2 . -> 3
      2 . -> 2 ....

      Au passage, je sais que ce genre de bidouille est faisable en Lisp, mais vous connaissez d'autres langages qui permettent de faire ce genre de truc inutile (juste pour le fun) ?
      • [^] # Re: 2+2=5

        Posté par  (site Web personnel) . Évalué à 2.

        Je continu mon petit jeu débile : voila la version en Common Lisp

        (let ((two 3))
            (defun my-two (stream char)
                (declare (ignore stream char))
                (setq two (- 5 two))
                two))

        (set-macro-character #\2 #'my-two)

        CL-USER> (+ 2 2)
        5
        CL-USER> 2
        3
        CL-USER> 2
        2
        CL-USER> 2
        3

        Et en C sous Windows ça donne quoi ? :-)
  • # Heisenbug...

    Posté par  (site Web personnel) . Évalué à 4.

  • # de plus...

    Posté par  (site Web personnel) . Évalué à 2.

    en plus des explications ci-dessus concernant les problèmes d'arrondis, pour afficher un double, il est préférable d'utiliser %lf ou %g.
    Le %f n'affiche le double qu'avec une précision de float... donc il y a encore des risques d'imprécisions.
    • [^] # Re: de plus...

      Posté par  . Évalué à 5.

      pas du tout efface.

      Extrait de la doc gnu libc :

      The `%f' conversion prints its argument in fixed-point notation, producing output of the form [-]ddd.ddd, where the number of digits following the decimal point is controlled by the precision you specify.

      et plus loin :
      Without a type modifier, the floating-point conversions use an argument of type double. (By the default argument promotions, any float arguments are automatically converted to double.)

      et man printf est d'accord :
      f,F The double argument is rounded and converted to decimal notation in the style [-]ddd.ddd, where the number of digits after the decimal-point character is equal to the precision specification.

      sinon, %g choisi automatiquement la representation la plus lisible suivant la valeur de l'argument (%f ou %e). et %lf est invalide.
      • [^] # Re: de plus...

        Posté par  (site Web personnel) . Évalué à 1.

        ok, j'ai rien dit...

        mais bon, j'ai souvenir que dans certains de mes programmes, je préférais voir le résultat en %g qu'en %f... enfin, ça dépend de tellement de paramètres...

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.