Journal Comment les programmeurs écrivent du code flottant ?

Posté par  .
Étiquettes : aucune
0
3
déc.
2007
Une petite question métaphysique m'a traversé l'esprit ce soir : Comment les codeurs codent avec du code flottant ?

Cela a l'air bien complexe. Si on reste sur PC x86, il y a les instructions x87, le SEE et les nombres sur 32, 64 et 80 bits, tout sachant que Intel calcule en interne sur 80 et non 64 bits pour les double au contraire de AMD.

Comment s'écrit un code ? Par essais erreurs ? On doit évaluer "à la main" les fuites de précision et écrire l'algorithme en conséquence ? J'imagine que c'est la démarche des codes scientifiques pour éviter d'utiliser des nombres étendus plus lent.

Ensuite, il y a bien des codeurs "3D" qui ne doivent jurer que par les 4 vecteurs floats SSE et les option type --fast-math qui se permettre de changer l'ordre des opération pour la vitesse (donc en cassant toutes étude de limitation d'erreurs de précision).

Le codeur 3D doit tout de même avoir besoin d'un minimum de précision et doit à un moment ou un autre faire attention à son code, si il ne veut pas trop d'aberration dans l'image.

Il doit y avoir aussi tous les intermédiaires entre ses 2 extrêmes.

D'ailleurs comment choisi-t-on d'utiliser un type d'arrondi plutôt qu'un autre ? Quel est l'intérêt de gérer les NaN ou les infinis qui ralentissent tellement un code c ?

Bref, je cherche des retours d'expériences sur la méthode choisie pour coder un algorithme utilisant les flottants.
  • # Comme tout le monde !

    Posté par  . Évalué à 6.

    Avec un clavier.
  • # en codant sur un bateau

    Posté par  . Évalué à 8.

    plus là
    --> []
  • # plop again

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

    Si on reste sur PC x86, il y a les instructions x87, le SEE et les nombres sur 32, 64 et 80 bits, tout sachant que Intel calcule en interne sur 80 et non 64 bits pour les double au contraire de AMD.

    les 80 bits du fpu ont du plomb dans l'aile puisque sur x86_64, tous les calculs se font en SSE2, cad en 64 bits maxi. Je ne sais pas historiquement quelle était la raison pour intel d'avoir 80 bits dans ses copro, mais en pratique ça a causé beaucoup plus d'emmerdes que ça n'a résolu de problèmes ( c'est bien pour ça qu'il y a une option -ffloat-store dans gcc pour les gens qui veulent des calculs conformes ieee)

    J'imagine que c'est la démarche des codes scientifiques pour éviter d'utiliser des nombres étendus plus lent.

    En général c'est double précision pour tout le monde, si cette précision ne suffit pas alors la bonne approche est de modifier l'algo plutot qu'utiliser des nombres avec une plus grande precision.

    D'ailleurs comment choisi-t-on d'utiliser un type d'arrondi plutôt qu'un autre ?

    Franchement je crois que tout le monde s'en fout mis à part deux trois psychopathes obsedés par ieee :)

    Quel est l'intérêt de gérer les NaN ou les infinis qui ralentissent tellement un code c ?

    Clairement detecter les erreurs de programmation, les divisions par 0, les sqrt(-1e-16) etc. Un autre truc qui ralentit monstrueusement le code c'est les nombres denormalisés, tous ceux qui font du traitement du signal avec des filtres récursifs en font l'experience un jour ou l'autre.
    • [^] # Re: plop again

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

      Q : D'ailleurs comment choisi-t-on d'utiliser un type d'arrondi plutôt qu'un autre ?

      R : Franchement je crois que tout le monde s'en fout mis à part deux trois psychopathes obsedés par ieee :)


      Oui mais si ces psychopathes écrivent des centrales inertielles pour une fusée spatiale, ou un avion de chasse ?
      Plus sérieusement, j'imagine qu'il y a plein d'applications où ce genre de choses ont leur importance ?

      « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

      • [^] # Re: plop again

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

        Quand tu parles de centrales inertielle pour une fusée spatiale, tu fais référence à ce qui s'est passé entre H+36 et H+42 secondes lors du lancement de la première Ariane 5 ? voir http://fr.wikipedia.org/wiki/Ariane_5#Premier_vol_.28vol_88_(...) et http://www-rocq.inria.fr/qui/Philippe.Deschamp/divers/ariane(...) pour les détails..

        \_o<

      • [^] # Re: plop again

        Posté par  . Évalué à 3.

        ou un missile anti-missile Patriot ou une fusée Ariane 5 ( voir par exemple http://amis.univ-reunion.fr/Conference/Complement/124_tourne(...) ).
        Ou même un avion de ligne... ça serait mal vu ;-)

        Il y a pas mal de recherche sur des solutions pour trouver de façon automatique l'imprécision du code, qui tendent à se substituer aux classiques "tests + modifications", à cause de l'augmentation toujours plus grande de la taille et de la complexité des programmes.

        Voir par exemple http://webdali.univ-perp.fr/~mmartel/Matthieu_Martels_Home_P(...) pour un résumé des techniques actuelles : étude formelle de la précision tout au long du programme, transformation sémantique des formules pour améliorer leur précision.
        • [^] # Re: plop again

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

          Juste pour préciser, dans mon post je ne voulais pas dire qu'on peut ignorer les erreurs d'arrondi, je parlais juste des "rounding mode" du fpu, tel que décrit ici http://en.wikipedia.org/wiki/IEEE_754#Rounding_floating-poin(...)

          D'ailleurs Java ne supporte que le "round toward zero", ce qui suggère que peu de gens ont besoin des 4 prescrits par la norme ieee
          • [^] # Re: plop again

            Posté par  . Évalué à 2.

            Sauf que les fameux modes peuvent avoir une petite influence sympa sur des mesures statistiques notamment, et qu'on peut tout à fait imaginer des cas tordus dans lesquels le changement d'un mode vers un autre entrainerait quelque chose de tordu dans le système final (bien que pour que ça influe beaucoup, j'imagine qu'il faudrait quand même qu'il soit sacrement instable à l'origine)

            Quand au Java, je ne mettrais ma main au feu quand à son utilisation massive en contrôle/commande.
            • [^] # Re: plop again

              Posté par  . Évalué à 4.

              En contrôle / commande peut être pas, mais dans les applis de transaction boursières, je peux t'assurer qu'on peut trouver du Java, à côté de C++ (puisque des libs spécialisées de calcul sont dispos en C++, mais de plus en plus en Java).

              Et là on est bien content de sortir les BigInteger et BigDecimal, qui sont nettement plus manipulables que Float et ses amis (notamment sur les arrondis). En effet, les règles de calcul sur les montants et les taux de change entre devises c'est obligatoirement sur 5 décimales, pour des montants qui montent facilement à quelques milliards (Yen par exemple).

              cf http://java.sun.com/javase/6/docs/api/index.html?java/math/B(...) et le petit package java.math, petit mais vital.
              Depuis l'API 1.5, le RoundingMode est défini dans un MathContext qui permet justement de choisir ... le contexte mathématique : IEEE 754R Decimal128, 64 ou 32.
      • [^] # Re: plop again

        Posté par  . Évalué à 4.

        Ou alors que les psychopathes doivent gerer les ransactions entre 2 systemes boursiers. Le Nasdaq qui utilise des Itanium partout a ete oblige d'ajouter des passerelles de conversion avec les autres places boursieres qui utilisent des architectures moins precises par exemple.
        • [^] # Re: plop again

          Posté par  . Évalué à 10.

          Je ne comprends pas trop. Les gros systèmes type IBM utilise des formats infini entier justement pour ne jamais avoir de problème. Le support est même hardware.

          "La première sécurité est la liberté"

          • [^] # Re: plop again

            Posté par  . Évalué à 4.

            Je plussoie, toute personne sensée n'utilise que des décimaux entiers pour tout ce qui est finance.
    • [^] # Re: plop again

      Posté par  . Évalué à 3.

      Pour ce qui est du type d'arrondi ça peut être utile dans certains domaines comme les contraintes numérique.

      Selon l'arrondissement on a ou on a pas des garanties de convergences et de ne pas éliminer des solutions qui satisfont les contraintes.

      Par contre j'admet que c'est un domaine très précis :)
    • [^] # Re: plop again

      Posté par  . Évalué à 1.

      Tiens, ça me rappel le problème que j'avais eu : https://linuxfr.org/~Snarky/25124.html

      Et même si je fais pas décoller de fusé, c'était bien casse pied, car c'était du travail sur une image (donc un pixel de décalage)...
      Personnellement, même si on a explique dans ce journal que c'était "logique", j'ai toujours pas réellement compris pourquoi une telle erreur.
      Du coup, j'ai toujours peur que ça se reproduise et j'ai du mal à "voir" comment penser mes équations de façon a éviter ce genre d'erreurs. :(
      • [^] # Re: plop again

        Posté par  . Évalué à 4.

        vu les nombres que tu manipules dans ton exemple, est-ce que tu ne cherches pas plutôt à faire du "fixed point", ce qui revient à faire des calculs entiers.

        Si tu prends tout tes nombres et que tu les multiplies par 16, tu as 4 bits après la virgules.

        "La première sécurité est la liberté"

        • [^] # Re: plop again

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

          est-ce que tu ne cherches pas plutôt à faire du "fixed point"


          Oulala, j'ai mis du temps à comprendre "fixed point", je pensais que tu parlais de point fixe et je ne voyais pas le rapport :-). "Virgule fixe" aurait été moins ambugu.
    • [^] # Re: plop again

      Posté par  . Évalué à 2.

      Un autre truc qui ralentit monstrueusement le code c'est les nombres denormalisés, tous ceux qui font du traitement du signal avec des filtres récursifs en font l'experience un jour ou l'autre.

      Quand est-ce qu'arrive ses nombres ? Je croyais que les fpu IEEE754 normalisait le nombre tout seul comme des grands.

      "La première sécurité est la liberté"

      • [^] # Re: plop again

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

        Justement c'est quand il n'y arrive plus que ces nombres arrivent, cad quand l'exposant est déjà à sa valeur minimum: ieee impose de pouvoir representer ces nombres très petits (cad ceux compris entre 1e-39 (2^-127) et 1e-46 (2^(-127-23))). En pratique ils ne servent à rien, à part à faire des "bugs" bizarres parce que les fpu ne sont pas du tout optimisés pour manipuler ces nombres (voire c'est gérè en soft), ça fait des ralentissements typiquement d'un facteur 1000 (mais comme tu le disais dans le journal, c'est souvent pareil pour les Nan et les inf)
    • [^] # Re: plop again

      Posté par  . Évalué à 5.

      En général c'est double précision pour tout le monde
      Une nouvelle tendance pour les processeurs ou il y a une difference en terme de temps de calcul entre precision simple et precision double:
      Effectuer une premiere estimation en precision simple, puis affiner en precision double.

      es 80 bits du fpu ont du plomb dans l'aile puisque sur x86_64, tous les calculs se font en SSE2, cad en 64 bits maxi.
      Au dela de cette demonstration douteuse:
      IEEE precise non seulement un format de stockage de nombre flottans (simple, double), mais aussi une precision sur un certain nombre d'operations, incluant evidement multiplication, division, racine carree, mais aussi certaines fonctions comme les fonctions trigonometriques.

      Comme les processeurs ne supportent generalement pas ces dernieres (et encore parfois meme pas la division), le resultat s'obtient par une serie de calcul (rappelez vous les developements limites, lagrange, polynomes de tchebychev....). Afin d'obtenir la precision specifiee par IEEE, il est necessaire d'utiliser une precision plus grande pour les calculs intermediaires. Ceci est le cas jusqu'au jours quelqu'un prouve qu'une telle precision n'est pas necessaire.

      La racine carree et la division est generalement prouvee en etudiant la valeur maximale de l'erreur. Mathematiquement, on sait prouver pour la totalite des valeurs sauf certains cas limites. Ces cas limites sont verifies experimentalement.

      Pour les autres, c'est plus du "Jusqu'a ce jour, en tenant compte des cas extremes identifies theoriquements, nous avons obtenu une erreur maximale de x. Il n'est pas impossible que l'on en ai manque dans la liste".

      Il existe des possiblites ou on peut utiliser des valeurs avec moins de precision pour les calculs intermediare, mais cela necessite plus d'operations, et a la fin cela ne vaut pas le coup.

      Dans le poste original:
      Comment s'écrit un code ? Par essais erreurs ? On doit évaluer "à la main" les fuites de précision et écrire l'algorithme en conséquence ?
      Tout a fait. Une etude de l'erreur est effectuee. Mais auparavant, on effectue une etude de stabilite: quel est la variation du resultat si l'on ajoute sur l'une des donnees en entree une petite valeur (epsilon).

      J'imagine que c'est la démarche des codes scientifiques pour éviter d'utiliser des nombres étendus plus lent.
      Reussir a prouver l'implementation de son calcul, c'est deja pas mal. On repasse generalement plus tard pour reduire la precision.
      • [^] # Re: plop again

        Posté par  . Évalué à 2.

        Effectuer une premiere estimation en precision simple, puis affiner en precision double.

        Pour la mise au point du calcul, j'imagine pas à l'utilisation ? Sinon, je ne vois pas l'interet de faire 2x ce calcul.

        Comme les processeurs ne supportent generalement pas ces dernieres (et encore parfois meme pas la division), le resultat s'obtient par une serie de calcul

        C'est encore la supériorité de la fpu x87 par rapport à SSE. Le x87 contient toute la trigo en hardware, contrairement à SSE. D'ailleurs, AMD était connu pour avoir une implémentation x87 musclé par rapport au processeur Intel qui favorisait plutot le SSE. Cela se voyait dans certain tests "scientifiques".

        Une etude de l'erreur est effectuee. Mais auparavant, on effectue une etude de stabilite: quel est la variation du resultat si l'on ajoute sur l'une des donnees en entree une petite valeur (epsilon).

        Cela revient à une étude d'interval, non ?

        Reussir a prouver l'implementation de son calcul, c'est deja pas mal. On repasse generalement plus tard pour reduire la precision.

        - Je trouve l'algo
        - Je prouve qu'il est stable numériquement
        - Je regarde les cas bizarre par expérimentation
        - si cela roule en double, j'essais de passer à la vecorisation ou au float 32 bits ?

        J'ai bon ?

        "La première sécurité est la liberté"

        • [^] # Re: plop again

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

          C'est encore la supériorité de la fpu x87 par rapport à SSE. Le x87 contient toute la trigo en hardware, contrairement à SSE. D'ailleurs, AMD était connu pour avoir une implémentation x87 musclé par rapport au processeur Intel qui favorisait plutot le SSE. Cela se voyait dans certain tests "scientifiques".

          Elle est quand même très relative cette supériorité: les instructions trigo hardware sont plus lentes que les implémentations equivalentes SSE (et elles n'ont pas la précision ieee)
          (moment pub: http://gruntthepeon.free.fr/ssemath/ )
        • [^] # Re: plop again

          Posté par  . Évalué à 3.

          « C'est encore la supériorité de la fpu x87 par rapport à SSE. Le x87 contient toute la trigo en hardware, contrairement à SSE. D'ailleurs, AMD était connu pour avoir une implémentation x87 musclé par rapport au processeur Intel qui favorisait plutot le SSE. Cela se voyait dans certain tests "scientifiques". »

          Euh. Pour moi, SSE et x87 n'ont pas du tout la même finalité. En SSE, tu manipules des blocs de 128 bits, sur lesquels tu effectues des opérations « vectorielles ». Donc en pratique, ça donne 4 multiplications simple précision faites en une seule fois ou bien 2 double précision. Mais en réalité, les mêmes instructions SSE servent aussi au calcul entier. À charge du processeur de savoir retrouver ses petits.

          Un co-processeur arithmétique « quelconque » est, lui, chargé « spécifiquement » de régler les problèmes liés aux flottants -- et donc intégrer des fonctions mathématiques câblées en dur ne semble pas déraisonnable. À mon avis, avec l'approche d'AMD (intégration future de GPU de plus en plus près des CPU, jusqu'au point de partager des unités fonctionnelles ou des registres/mémoires), et de façon générale la « généralisation » des tâches des GPU, on va tout bêtement redécouvrir les joies des « coprocesseurs arithmétiques » version carte à enficher dans un port PCI-E...
          • [^] # Re: plop again

            Posté par  . Évalué à 4.

            A ce propos, un lien:
            http://www.gpgpu.org
            Ils s'amusent comme un fou.
          • [^] # Re: plop again

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

            Pour moi, SSE et x87 n'ont pas du tout la même finalité. En SSE, tu manipules des blocs de 128 bits, sur lesquels tu effectues des opérations « vectorielles ».

            Mais pas seulement, toutes les instructions sse vectorielles ont un pendant scalaire (qui ne manipule que le premier element de ton petit vecteur, et qui n'a pas les contraintes d'alignement mêmoire des instructions vectorielles), on peut donc se passer totalement du x87 sans avoir de penalité de performance (c'est d'ailleurs ce que fait gcc quand on lui indique fpmath=sse : il remplace les instructions x87 par du SSE scalaire , il ne vectorise rien). Sur x86_64 , et sur darwin/intel, le x87 est d'ailleurs banni.
          • [^] # Re: plop again

            Posté par  . Évalué à 1.

            "Mais en réalité, les mêmes instructions SSE servent aussi au calcul entier. "

            euh, non il me semble pas.

            Le SSE2 à ajouter les doubles. SSE3 a définit des opérations binaire sur le vecteurs 128 bits. Je ne pense pas qu'il y est des opérations entières.

            Le SSE2 introduit aussi des opérations flottantes scalaires.

            "La première sécurité est la liberté"

            • [^] # Re: plop again

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

              pas tout a fait:
              SSE = simple precision seule, flottant ou scalaire, *avec* les operations logiques et les comparaisons. Pour les entiers (64bits max), il faut passer par le mmx.

              SSE2 ajoute les entiers (on n'a plus besoin de mmx) , et la double precision

              SSE3 ne sert à rien, ni SSSE3, SSE4a, ... (intel a un certain talent pour empiler les multiples revisions qui n'apportent à chaque fois quasiment rien de nouveau mis a part des trucs hyper specialisés pour decoder du h264 ou je ne sais quoi)
        • [^] # Re: plop again

          Posté par  . Évalué à 4.

          apparement ma reponse est dans la nature quelque part.

          Donc je recommence:
          Pour la mise au point du calcul, j'imagine pas à l'utilisation ? Sinon, je ne vois pas l'interet de faire 2x ce calcul.

          Je parlais bien de l'implementation. et je n'ai pas dit "refaire" le calcul mais "affiner".

          Pour illustration, prenons un exemple concret: l'implementation de la fonction inverse en double precision sur une machine n'implementant pas la division directement. Pour info le calcul de a/b se fait en calculant 1/b puis en effectuant la multiplication avec a. Donc 1/b n'est pas la simple division de 1 par b.

          Imaginons que tu saches calculer 1/b avec une faible precision. On dira que c'est y.

          On note que la fonction f(x)=1/x - b a une racine pour x=1/b
          En utilisant Newton-Ramson pour trouver la racine de f, on obtient la serie:
          x_{n+1} = x_n(2-b*x_n)
          Cette serie converge vers 1/b, ce que l'on cherche.
          La convergence dans notre cas est quadratic, et le nombre d'etapes necessaire pour obtenir un resultat avec une prevision inferieure a une erreur donnee depend de la difference entre x_0 et 1/b.
          A noter que cette serie n'utilise que des multiplications et des additions/soustractions.

          Plus x_0 est proche de 1/b, moins d'etapes sont necessaires pour obtenir 1/b avec une precision voulue.

          Dans beaucoup de CPU, la fonction inverse est implementee de cette facon, le CPU ne fournissant qu'une valeur approchee de la division...
          Idem pour la racine carree.

          C'est la meme methodologie utilisee pour la methode hybride simple et double precision:
          - La serie est etablie sur papier et l'etude de la convergence de l'erreur etablit le nombre maximal d'iteration a effectuer pour obtenir un resultat correct en double precision pour un x_0 initial avec une precision inferieure ou egale a ce que l'on aurait au pire avec une implementation en simple precision. Evidement, on tient compte du fait que la multiplication et addition sont arrondies.

          - Le calcul en precision simple etabli x_0 afin d'avoir une valeur approchee dont on majore l'erreur.
          - On applique les n iterations necessaire

          C'est effectivement plus complexe a etablir que de tout faire en double, mais ca peut avoir un gain enorme dans certains cas.

          C'est encore la supériorité de la fpu x87 par rapport à SSE.
          Tout depend de la methode d'evaluation de ce qui est meilleur a un autre. Cependant:
          - En terme de transistor, n'implementer que des multiplications et addition, c'est moins couteux.
          - De meme, si tu as besoin d'une precision plus fine que simple mais pas autant que double, tu y gagnes.
          - Si quelqu'un trouves une meilleur implementation, ben c'est logiciel. Idem pour les bugs (le bug du Pentium sur la FP etant une mauvaise implementation de la fonction "approchee" de 1/x).
          - Si pour un calcul unique c'est pas superieur a une fonction hardware, le fait que la multiplication et l'addition peut etre implemente dans un micropipeline, appliquer la meme fonction sur plusieures valeurs peut etre plus rapide que l'application sequentielle de cette operation sur chacune des valeures.

          Cela revient à une étude d'interval, non ?
          Oui mais on verifie deja dans les cas dont on sait que les sous operations de la fonction ont des discontinuites avant de se prendre la tete a verifier quelque chose dont on est pas sur que ce soit verifiable.

          J'ai bon ?
          Oui, mais je rajouterais les etapes initiales:
          - Je definis quels sont mes besoins en terme de precision.
          - Je verifie que quelqu'un l'a pas deja fait, parce que une prise de tete enorme s'annonce
          A la fin:
          - Je cree une doc avec mes formules mathematiques comme ca cela servira a quelqu'un d'autre. Les capacites de formatage etant limite dans un code source pour prouver mon erreur, faire ca dans un fichier a part. Genre avec LaTeX.
  • # Et les bibliothèques ?

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

    Le plus simple est encore d'essayer de ne pas réinventer ce qui a déjà
    été codé par des bons et testé et éprouvé par le plus grand nombre.

    Je pense par exemple à GSL (GNU Scientific Library), ou à fpconst pour
    ceux qui font du Python, etc.
    • [^] # Re: Et les bibliothèques ?

      Posté par  . Évalué à 2.

      Je ne vois pas bien comment ta bibliothèque peut écrire des équations à ta place.

      Imagines n'importe quel filtre numérique.

      "La première sécurité est la liberté"

      • [^] # Re: Et les bibliothèques ?

        Posté par  . Évalué à 4.

        Non, mais la bibli fournit des primitives de haut niveau pour écrire ces équations, pour qu'on n'ait pas à se soucier de la manière dont le processeur les calcule. D'ailleurs, à moins de vouloir faire des optimisations de la mort, ça doit être la seule façon d'avoir un code un tant soit peu portable et de garder des cheveux sur la tête.
        • [^] # Re: Et les bibliothèques ?

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

          Pour les cheuveux tu peux aussi coder avec un casque. Ça à le double avantage de te sauver la vie quand l'envie subite de mettre un violent coup de boule à ton écran à tube cathodique te prend.
          • [^] # Re: Et les bibliothèques ?

            Posté par  . Évalué à 1.

            Ça à le double avantage de te sauver la vie quand l'envie subite de mettre un violent coup de boule à ton écran à tube cathodique te prend.

            D'où l'intérêt des écrans LCD.
  • # Quelques infos

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

    Salut,

    Comment s'écrit un code ? Par essais erreurs ? On doit évaluer "à la main" les fuites de précision et écrire l'algorithme en conséquence ?

    Il existe quelques règles que je ne saurai énoncer exhaustivement. Exemple : ne pas utiliser d'opérande sur un petit et un grand nombre (1 + 10^100 est crétin). Il faut réordonner une fonction en conséquence. Exemple pratique : résolution d'une équation du 2e degré :
    http://fr.wikipedia.org/wiki/%C3%89quation_du_second_degr%C3(...)

    J'imagine que c'est la démarche des codes scientifiques pour éviter d'utiliser des nombres étendus plus lent.

    Euh, les éviter je sais pas, c'est très pénible de calculer en virgule fixe (avec des nombres entiers). Il existe un décodeur Ogg/Vorbis en virgule fixe, mais je vois pas le rapport, on s'aggare :-)

    changer l'ordre des opération pour la vitesse

    Je ne vois pas en quoi l'ordre change la vitesse. Utilise des SIMD ou non, ça change les perfs oui. Mais on ne change pas l'ordre du calcul. On reformule pour permettre la vectorisation. Au passage, les derniers gcc vectorisent tout seul !

    Sachez que GCC ne modifient pas les calculs sur les flottants car justement l'ordre des opérandes joue énormément sur la précision du résultat. Il ne pas simplifier x*2.0/2.0 par exemple.

    D'ailleurs comment choisi-t-on d'utiliser un type d'arrondi plutôt qu'un autre ?

    Selon le résultat attendu ?

    Quel est l'intérêt de gérer les NaN ou les infinis qui ralentissent tellement un code c ?

    Les gérer ? Comment ça ? Il suffit de vérifier à la fin qu'on n'a pas de NaN, -INF ou INF je pense. Si on sait ce qu'on fait, on ne trimballe pas de NaN.

    --

    Pour aller un peu plus loin, il existe la bibliothèque MPFR :
    http://www.mpfr.org/

    Enfin, un bilbliothèque qui calcule sur des intervalles : MPFI stands for Multiple Precision Floating-point Interval library.
    http://perso.ens-lyon.fr/nathalie.revol/software.html
    • [^] # Re: Quelques infos

      Posté par  . Évalué à 4.

      c'est très pénible de calculer en virgule fixe

      Je ne connais pas bien le domaine mais comme il existe des calculs en "entier infini", il existe aussi des calculs en flottant infini.

      Je ne vois pas en quoi l'ordre change la vitesse.

      Par ce que a+b+c+d sera plus lent que (a+b)+(c+d) car tu enlèves une dépendance read-after-write.

      Au passage, les derniers gcc vectorisent tout seul !

      Enfin, si on l'aide beaucoup beaucoup beaucoup...

      Sachez que GCC ne modifient pas les calculs sur les flottants car justement l'ordre des opérandes joue énormément sur la précision du résultat. Il ne pas simplifier x*2.0/2.0 par exemple.

      C'est le cas par défaut. Mais cela n'est plus vrai avec l'option --fast-math.


      D'ailleurs comment choisi-t-on d'utiliser un type d'arrondi plutôt qu'un autre ?

      Selon le résultat attendu ?


      C'est choix par équation ou par opération ?

      Les gérer ? Comment ça ? Il suffit de vérifier à la fin qu'on n'a pas de NaN, -INF ou INF je pense. Si on sait ce qu'on fait, on ne trimballe pas de NaN.

      Par exemple, "<" définit tous les comportements à avoir avec NaN < NaN, NaN < nombre, etc... Ce qui est tellement lent qu'il existe des intrasecs pour faire un test qui se fout des NAN. Je me demandais donc quelle était l'utilité. En gros, le but est de faire de la détection d'errreurs ?

      "La première sécurité est la liberté"

      • [^] # Re: Quelques infos

        Posté par  . Évalué à 1.

        « Par ce que a+b+c+d sera plus lent que (a+b)+(c+d) car tu enlèves une dépendance read-after-write. »


        Non. Enfin, je trouve que tu as mal formulé. Suivant le langage, tu as des certitudes ou pas concernant l'évalutation des opérandes. Par exemple, avec C, tu n'as aucune certitude quant à l'ordre d'évaluation de res = a + b + c +d. En Java, il est spécifié que les opérandes sont évalués de gauche à droite.

        Maintenant évidemment, « dans le doute », tu peux parenthéser. Mais normalement, l'utilisation de transformations optimisantes dans les compilateurs est justement censer t'éviter de tout faire à la main.

        Typiquement, avec la mise sous forme SSA, les fausses dépendances (WAW, RAR, WAR) peuvent être éliminées, et les opérations indépendantes mises en valeur et réordonnancées -- et ce, quel que soit l'ordre souhaité par le programmeur à la base. Tout ceci sans parler des processeurs qui exécutent les instructions dans le désordre, bien entendu.
        • [^] # Re: Quelques infos

          Posté par  . Évalué à 4.

          En C, c'est parfaitement interdit de toucher à l'ordre des opérations même en entier. Le problème se pose avec les overflows possibles.

          J'avais fais un paquet de tests, il y a quelques années et gcc ne faisait rien du tout dans ce domaine.

          Tout ceci sans parler des processeurs qui exécutent les instructions dans le désordre, bien entendu.

          Sauf avec les dépendances RAW où tu ne peux rien faire du tout !

          "La première sécurité est la liberté"

          • [^] # Re: Quelques infos

            Posté par  . Évalué à 2.

            Qu'entends-tu par « interdit » ? Le compilateur comprend parfaitement ce que tu veux dire lorsqu'il est écrit

            res = (a + b) + (c + d)

            Tu n'as aucun moyen de savoir si (a+b) sera effectué avant (c + d), mais sinon, le compilateur « comprend » les priorités. Par contre, si par une analyse statique quelconque il voit que les parenthèses sont inutiles, et qu'il pourrait s'arranger différemment, il ne va pas se gêner.

            Le vrai problème de l'ordre d'évaluation se pose plutôt pour des choses du genre

            res = f1() + f2() + f3() + f4();

            (en supposant que les f*() sont définies dans une unité de compilation différente)

            Dans ce cas-là, si toi, programmeur, tu demandes un ordre spécifique en parenthésant, je ne suis pas certain que le compilateur s'amuse toujours à changer l'ordre des opérations ...
            • [^] # Re: Quelques infos

              Posté par  . Évalué à 2.

              Il pourrait mais il ne le fait pas.

              a+b+c+d équivaux à (((a+b)+c)+d)

              C'est à vérifier mais je suis sur à 90% que le compilo C ne doit pas y toucher.

              Car a+b+c+d veut en fait dire :
              trunc<32>(trunc<32>(trunc<32>(a+b)+c)+d)

              Ce qui est non équivalent à :
              trunc<32>(trunc<32>(a+b)+trunc<32>(c+d))

              "La première sécurité est la liberté"

              • [^] # Re: Quelques infos

                Posté par  . Évalué à 2.


                Car a+b+c+d veut en fait dire :
                trunc<32>(trunc<32>(trunc<32>(a+b)+c)+d)

                Ce qui est non équivalent à :
                trunc<32>(trunc<32>(a+b)+trunc<32>(c+d))


                Heu... en calcul entier c'est la même chose. La seule différence pourrait avoir lieu dans l'état des flags à la fin, mais les flags ne sont pas testables directement en C.
            • [^] # Re: Quelques infos

              Posté par  . Évalué à 0.

              si tu fais "c=a+b-a" en C, avec des flottants, le compilateur fera vraiment a+b-a. il ne cherchera pas à simplifier.

              Alors a+b+c+d ne sera pas transformé en (a+b)+(c+d) par le compilo. Et même si le CPU effectue plusieurs opération en parallèle, il ne réordonne JAMAIS les instructions, il "délivre" les instructions dans le même ordre que celui avec lequel elles arrivent.
              • [^] # Re: Quelques infos

                Posté par  . Évalué à 3.

                « si tu fais "c=a+b-a" en C, avec des flottants, le compilateur fera vraiment a+b-a. il ne cherchera pas à simplifier. »

                Non. Ce genre de comportement (éliminer les 'a') est totalement dépendant du compilateur. Par exemple, avec la plâtrée de transformations optimisantes qu'on trouve dans icc par exemple, je ne suis pas certain que ce que tu affirmes soit vrai (je vais tester demain, je te tiendrai au courant :-) ).

                En fait tout dépend de l'ordre dans lequel les transformations ont été effectuées : une fois toutes les fausses dépendances éliminées (grâce à SSA ou autre), on peut très bien couper les branches inutiles de l'AST, et examiner plus en détail les expressions arithmétiques de chaque noeud restant de l'arbre.

                Évidemment, sur des cas bien plus complexes (car « cachés »), l'optimisation n'aura pas forcément lieu.

                « Et même si le CPU effectue plusieurs opération en parallèle, il ne réordonne JAMAIS les instructions, »

                Si. Typiquement sur les x86, les instructions sont exécutées dans le désordre du moment qu'il n'y a pas de dépendance de donnée (les dépendances WAR dont parlait nicO) et que les unités fonctionnelles sont disponibles. Ceci évidemment sans parler de la spéculation sur les branchements, qui provoquent le vidage du pipeline lorsque la machine a mal spéculé.

                « il "délivre" les instructions dans le même ordre que celui avec lequel elles arrivent. »

                Non, il y a un Reorder Buffer (ROB) qui se charge de remettre le résultat des instructions dans l'ordre pour garder la sémantique du programme, mais avant ça, les instructions peuvent parfaitement avoir été exécutées dans un ordre différent.
                • [^] # Re: Quelques infos

                  Posté par  . Évalué à 2.

                  icc est connu pour inclure --fast-math dans -o2, ce que ne ce permet pas gcc.

                  Sinon, on semble dire la même chose. Quand il y a execution en parrallèle on ne peut plus vraiment parler d'ordre. Le flot d'instruction est déterminé par leur liaison par les registres et par les accès à la mémoire.

                  "La première sécurité est la liberté"

                  • [^] # Re: Quelques infos

                    Posté par  . Évalué à 2.

                    en fait, je crois qu'avec le compilateur de Visual Studio 6, il fallait écrire (a+b)-a, sinon l'expression était simplifiée. Donc ça dépend bien du compilateur et des options d'optimisation.
                    • [^] # Re: Quelques infos

                      Posté par  . Évalué à 2.

                      Oui mais c'est en violation de la spec du C.

                      "La première sécurité est la liberté"

                      • [^] # Re: Quelques infos

                        Posté par  . Évalué à 2.

                        Dans ce cas, la spec C autorise tout compilateur a faire ce qu'il veut tant que le resultat est similaire a ce qui aurait ete obtenu en executant la version fournie par le code. Si une optimization existe et prouve que, en fonction de contraintes decouvertes lors de la compilation a+b-a est bien egal a b, alors le compilateur ne viole rien.

                        Par contre bon courage.
                        • [^] # Re: Quelques infos

                          Posté par  . Évalué à 2.

                          C'est exactement ça :)

                          (a+b)-a
                          se comprend
                          ((a+b) & 0xFFFFFFFF - a) & 0xFFFFFFFF

                          Or c'est différent de b & 0xFFFFFFFF selon la valeur de a et de b.

                          "La première sécurité est la liberté"

                          • [^] # Re: Quelques infos

                            Posté par  . Évalué à 1.


                            (a+b)-a
                            se comprend
                            ((a+b) & 0xFFFFFFFF - a) & 0xFFFFFFFF

                            Or c'est différent de b & 0xFFFFFFFF selon la valeur de a et de b.


                            Non ce n'est pas différent. Ou alors j'attends des contre-exemples :)
                            • [^] # Re: Quelques infos

                              Posté par  . Évalué à 5.

                              l'idée de base est que "a+b" est un résultat sur 33 bits et qu'il y a perte. Tu t'en sors avec les propriétés des modulos, j'ai l'impression.
                              Par ce que en math non signé, 0x0-0x1 = 0xFFFFFFF; n'a pas vraiment de sens.

                              "La première sécurité est la liberté"

                    • [^] # Re: Quelques infos

                      Posté par  . Évalué à 3.

                      J'ai fait le test avec icc. Voilà ce qui en résulte :

                      code :


                      /* elim.c */
                      double elim(double a, double b) {
                      __double tmp;
                      __tmp = a+b-a;
                      __return tmp;
                      }

                      /* elim2.c */
                      double elim2(double a, double b) {
                      __double tmp;
                      __tmp = (a+b)-a;
                      __return tmp;
                      }



                      ; code généré par icc quelle que soit la version
                      ; xmm0 = a, xmm1 = b
                      __subsd xmm0, xmm0
                      __addsd xmm1, xmm0
                      __ret

                      ; code généré par gcc
                      ; elim2() :
                      __addsd %xmm0, %xmm1
                      __subsd %xmm0, %xmm1
                      __movapd %xmm1, %xmm0
                      __ret

                      ; elim()
                      __movl %esi, %eax
                      ret


                      Comme quoi, sur des cas simples, les compilateurs se débrouillent ...
                      • [^] # Re: Quelques infos

                        Posté par  . Évalué à 1.

                        Ne confonds pas la norme et la toutouille des compilos.

                        "La première sécurité est la liberté"

                        • [^] # Re: Quelques infos

                          Posté par  . Évalué à 2.

                          Oui, il faut utiliser -fltconsistency
                          Ca a un avantage dans le sens ou cela suit strictement la norme.
                          Dans le cas ci-dessus, c'est plus precis.

                          L'inconvenient c'est que parfois c'est moins precis aussi. Par exemple sur Itanium, a*b+c sera desormait encode avec une multiplication et une addition separee, plutot qu'avec un fma (fused multiply and add). Or comme il n'y a que fma de disponible, on se retrouve avec:
                          temp <- a*b + 0
                          res <- temp*1+c

                          au lieu de:
                          res <- a*b + c

                          La difference c'est qu'il y a potentiellement deux erreures d'arrondis dans le premier cas, au lieu d'une seule dans le second.

                          Maintenant tu as tout a fait raison:
                          Ne confonds pas la norme et la toutouille des compilos.
                          Le compilo fait ce qu'on lui demande. Il faut lui dire ce que l'on attend de lui.

                          Mais tout n'est pas bon a jeter dans la toutouille d'un compilo, et on est parfois bien content que le compilo n'applique pas la norme dans sa version stricte.... Sinon adieu le multithreading.

                          Imagine que a soit une variable qui est accessible par plusieurs thread et que ton code utilises par exemple un semaphore.

                          Maintenant tu as la sequence:
                          c=e+f
                          g=h+i

                          Tu n'utilises pas a. Donc pas de pb. Oui, parce que les compilos sont gentils et ils ont surtout autre chose a faire que de nous embeter avec la lecture strict de la norme.

                          Parce que d'apres la norme, il est tout a fait correcte pour un compilo de generer la sequence suivante:

                          j=a
                          c=e+f
                          g=h+i
                          a=j

                          La sequence etant equivalente a la precedente, c'est valable. Tu vois le drame? Ben des fois Gcc fait des trucs bizarre dans ce genre. Linus c'est un peu fache.

                          C'etait juste pour ouvrir un peu le debat entre l'adequation d'un compilateur a une norme. Meme si les deux cas sont differents.
  • # C'est la précision que tu veux

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

    En simplifiant :
    - 32 bits, c'est une precision à 10 exp -6 près, genre pi=3.14159xxxxxxxxxxx (x n'étant pas la valeur exacte de pi)
    - 64 bits, c'est une precision à 10 exp -15 près, genre pi=3.14159265358979xxxxx (x n'étant pas la valeur exacte de pi)
    - 80 bits, c'est une precision à 10 exp -20 près, genre pi=3.14159265358979323846xx (x n'étant pas la valeur exacte de pi)

    Donc ca dépend si le nombre que tu veux à la fin soit précis ou pas.
    Rajoute que pour chaque opération, tu perds la moité de précision (pour schématiser) au moins, il faut que tu prévois de la marge dans ta précision.
    Par exemple, si en shématisant (je schématise hein, pour les puristes je sais que c'est plus complexe, et mon exemple n'est pas la réalité) :
    (float)0.000005 + (float)0.000005 --> 0.00001 + 0.00001 (ben oui, le float a du mal à stocker la précision) --> 0.00002
    alors qu'avec un double :
    0.000005 + 0.000005 --> 0.00001

    Dans cet exemple, il faut que tu sois conscient que le chiffre fourni est juste a + ou - 0.00001. Généralement on affiche pas autant de chiffres après la virgule, pour justement éviter ce + ou -.

    Bref, en gros si tu calcules des valeurs avec juste très peu de chiffres importants pour toi --> 32 bits, sinon 64 bits.

    (dans le même style, j'adore les sondages précis à 0.1% près, alors que le dégré de précision des sondages est de + ou -2 à 3 % en général, chiffre indiqué par IPSOS dans son papier mais oublié par les journalistes qui veulent simplifier : un sondage a 51.3% + ou - 3% pour une personne veut juste dire que le sondage estime entre 48.3% et 54.3% le score du candidat, donc que le chiffre peut être inférieur à 50%, chose oubliée...)
  • # Cobol Rulez

    Posté par  . Évalué à 3.

    Si il y a un domaine où tous doivent tomber juste au quart de poil près, c'est les calculs en informatique de gestion (banque, assurance). Or lors de la soustraction de nombres proches en virgule flotante les risques d'erreurs sont important. C'est pour ça que ces calculs sont fait en virgule fixe.

    Et un langage qui le permet facilement c'est le Cobol énormément utilisé en gestion depuis des années et qui continuera encore...
  • # Ecrire du code avec des flottants

    Posté par  . Évalué à 10.

    Une petite question métaphysique m'a traversé l'esprit ce soir : Comment les codeurs codent avec du code flottant ?

    Réponse de gascon : Ca dépend du code.
    En fait il y a plusieurs cas de figures :
    - Ranapété : Je me fous de ce que font les flottants dans la mesure ou ils me donnent un résultat suffisament juste rapidement. C'est le cas par exemple en programmation 3D (ou on peut toujours s'arranger à posteriori pour éliminer les cas problématiques en corrigeant les cartes ou les modèles 3D
    Par exemple la plupart des moteurs de Carmak ont de gros problèmes de Z aliasing, mais ca ne se voit pas sur les cartes car les cas à problème sont soigneusement évités) C'est aussi le cas dans évaluation biologique (ou la norme sanitaire est entre 10 at 1000 fois inférieure à la dose toxique, donc on se moque un peu de savoir si on a 0,0800 µg de saleté ou 0,07999954215 ) et dans la plupart des calculs fait à partir de prélèvement capteurs en temps réel (le capteur a une précision de 0,01 alors le 17ème chiffre après la décimale est de toutes les façons faux)

    - Ranapété du moment que ca concorde : Là je me moque de savoir ce que font les décimales parceque 1°) de toutes les façon au final je vais arrondir 2°) j'ai largement assez de précision pour m'en sortir et/ou 3°) de toutes les façon je suis sur des valeurs qui fluctuent tellement que je vais me mettre d'accord avec un tiers et qu'on va fixer ensemble un résultat de connivence. Tout ce qui m'interesse c'ets qu'à la fin de la journée j'ai des résultat cohérent avec mes choix et vérifiables par une authorité.
    par exemple une comptabilité en francs que je veux passer en euros. Il faut juste que la somme de toutes mes opérations converties en euros soit égale au réel des comptes converti en euros. En d'autres termes si Somme(Ai)=B je veux Somme(conv(Ai))=conv(B) avec une fonction de converion conv() qui soit identique d'un coté et de l'autre de l'opération et qui soit normalisée et approuvée par un organisme de controle le cas échéant. C'est ce qui se passe dans les chambres de compensation internationales quand les entiers à décimales fixées commencent à plus en pouvoir (additionner des centainnes de milliers d'opérations au dix-milième d'euros près ca devient coton)

    - Ca serait bien que deux ordinateurs différents trouvent le même résultat.
    La le truc c'est pas vraiment que l'on veuille que résultat soit super précis, on veut juste qu'il soit vérifiable. En d'autres termes si une suite de calculs donne come résultat 0.73244298 ca serait bien que la même suite d'opération donne le même résultat sur l'ordinateur d'a coté. Pourquoi ? parceque ca permet de savoir que l'on est pas en train d'atteindre une condition limite (genre quand on trouve des résultats trop différents d'un ordinateur à l'autre, il est temps de changer d'algorithme), ca permet de vérifier qu'un ordi n'est pas en train de rendre l'ame (tiens ca fait quatre fois que l'ordi 1 trouve un résultat qui diverge, apelle le broker je signe le certificat de décès) ou qu'un bit d'execution ou de données n'a pas été perturbé par un effet magnétique farceur (rarissime mais réel sur terre, plus courant dans les satellites et autres engins spatiaux) etc. C'est là que les IEEE rentrent en ligne de compte en faisant un choix abitraire parfois faux, mais vérifiable.

    - 100% pure vraies décimales dedans. Là il s'agit de ne pas faire d'erreur d'arrondi du tout. En d'autres termes l'arrondi n'est fait que sur la dernière décimale stoquée et seulement au moment de stoquer le résultat final. C'est là que les entiers 64 bits calculés sur 80 bits rendent bien service. En d'autres termes on rentre des chiffres exacts avec une précision x, tous les calculs sont faits en précision y (y > x) suffisament grande pour qu'il n'y ait pas de perte d'information avant que les calculs ne soient finis. Et quand les calculs sont finis on repasse en précision x avec un arrondi dans les règles de l'art. Là le boulot du programmeur et de vérifier que l'on ne peut pas perdre d'information dans les opérations avant la sortie du résultat. C'est ce que l'on a dans les calculs par éléments finis simple, ou dans les simulations moléculaires limitées a quelques atomes/molécules. Ce qui nous ammène a l'utilisation la plus chiante des flottants :

    - Bonjour, j'additionne des fourmis avec des éléphants.
    Là on a des trucs du style 45E32 + 0.1E-14 + 0.2E-12 + 0.4E-12 + ... (plusieurs centaines de millions de nombre à faible exposant) ...+ 0.7E-11
    Laissé à lui même avec des arrondis classiques l'ordinateur donnera un résultat très différents suivant l'ordre dans lequel il effectuera les opération.
    Dans pas mal de cas on peut s'en sortir de façon élégante en prioritisant les opérations. C'est à dire en n'essayant d'éviter de faire des opérations entre des nombres qui ne sont pas du même ordre. En d'autres termes on garde l'unité flottante du processeur, mais on lui passe les opérations dans le bon ordre pour que ca se passe bien. Dans les autres cas il faut faire autrement. Par exemple faire appel à des bibliothèques extérieures boostées, faire mumuse avec les registres, la carry et les différents flags soi-même ou passer par des modes de calculs différents (en passant sur des entiers très long ou en utilisant des fonctions vectorielles ou encore ou faisant les opérations sur des "blocs" de précisions différentes que l'on récolle à la fin.)

    Bref, ca dépend du problème à résoudre. De façon générale les calculs en flottants sont casse-pieds, on évite donc autant que possible. Et quand on est obligé de s'en servir (pour des raisons de vitesse ou de précision) on ne surveille les décimales que si on en a vraiment, vraiment besoin.
    • [^] # Re: Ecrire du code avec des flottants

      Posté par  . Évalué à 2.

      ça au moins, c'est complet :)

      Je peux résumé par :
      "On veut de la répétabilité, et parfois on veut une idée précise de la précision." ?

      Par contre, concernant les arrondis, l'utilisation de fonction explicite ou des mode de fpu influe sur quoi ? Uniquement la vitesse ? Ou il existe encore des subtilités ?

      Comment utilises-t-on les modes "fast" ? En général, ces modes sont taxés de mal calculer. Je crois que l'itanium en a un. Les cartes graphiques utilisent aussi ces modes.

      Si j'ai bien compris, il ne détecte pas les NaN dans le pipelines flottant pour éviter de gérer des interruptions en plein milieu. Mais si j'ai bien compris d'autres interventions, cela n'est génant que pour la detection d'errreur et non pour le fonctionnement nominal. J'ai bon ?

      "La première sécurité est la liberté"

      • [^] # Re: Ecrire du code avec des flottants

        Posté par  . Évalué à 1.

        A propos des arrondis et de la vitesse:
        Cela depend. Pour des operations elementaires genre addition, multiplication et al, cela ne devrait pas influencer.
        La ou ca devient un peu plus tordu c'est pour les fonctions trigonometriques: Comme certains resultats doivent arrondis car la precision d'un flottant, simple ou double ne suffit pas a stocker la valeur exacte, le resultat est arrondi. Cependant: Le resultat arrondi doit etre le meme que si tu arrondissais la valeur exacte de la meme facon.

        Si tu es capable de faire une operation dans un format avec une plus grande precision, et que l'implementation de ta fonction est capable de garantir un resultat interne avec une assez bonne approximation, alors tu sais que quelque soit la regle d'arrondissement utilise pour stocker ta valeur arrondie, tu es coherent avec l'arrondissement du resultat reel.

        Si ton implementation arrive a cette precision, alors la regle d'arrondissement n'influe pas la vitesse. Par contre, si tu ne peux pas, alors tu peux avoir a implementer differentes implementations pour chaque cas, pas toujours tous de la meme vitesse.

        Dans la pratique, choisit la regle d'arrondissement qui convient a ce que tu cherches a calculer, pas pour des questions de vitesse d'execution.

        En général, ces modes sont taxés de mal calculer.
        Ils calculent tres bien avec les regles etablies. C'est juste que ces regles ne correspondent pas a IEEE. Donc pour etre plus precis:
        En general, ces modes sont taxes de ne pas calculer comme IEEE le definit.

        Si j'ai bien compris, il ne détecte pas les NaN dans le pipelines flottant pour éviter de gérer des interruptions en plein milieu.

        Il y a deux modes "Fast": Y en a 1 c'est pour le processeur, l'autre c'est le compilateur.

        Pour le processeur, c'est le "Flush to zero": Considere un nombre denormalise comme etant 0 et ne vient pas faire une exception logicielle pour deleguer le calcul a une implementation logicielle.

        Pour le compilateur, c'est tout une suite d'optimisations qui ne sont pas valables par rapport a la norme mais dont beaucoup de programmes se foutent: Propagation de constantes, simplification d'operations, combination d'operations...
        • [^] # Re: Ecrire du code avec des flottants

          Posté par  . Évalué à 2.

          Pour le processeur, c'est le "Flush to zero":

          C'est uniquement ça ? Il me semblait que cela pouvait aller bien plus loin. Par exemple, les règles d'arrondies un peu assouplie (moins/pas de garde bit).

          A moins que le mode dont tu parles, c'est le fonctionnement sur x86 ?

          "La première sécurité est la liberté"

          • [^] # Re: Ecrire du code avec des flottants

            Posté par  . Évalué à 2.

            Je viens de me retaper la doc...
            Donc en fait y a 2 autres cas qui sont lies a l'implementation de frcpa et frcsqrta, qui fournissent une approximation de la fonction inverse et racine:

            - La definition de l'architecture garantie que ces deux instructions retournent une valeur approchee dont l'erreur maximale est fixee.
            - Si la valeur est exacte un bit est mis a 1 pour signaler qu'il n'y a pas lieu d'affiner
            - Si l'implementation (en gros une micro-architecture) ne peut pas fournir une valeur approchee satisfaisant l'erreur max, alors le proc doit faire un "Software Assist Fault" qui delegue a une implementation logicielle la tache de calculer la valeur reelle (ce qui mettre le but de precision a 1).

            J'ai pas encore vu (mais j'ai pu le louper) d'implementations de l'Itanium qui ai eu besoin de demander assistance pour ces deux instructions.

            Pour ce qui est des arrondis un peu assouplie (ou parfois plus stricte), c'est une affaire d'implementation logicielle des fonctions, pas du processeur.
      • [^] # Re: Ecrire du code avec des flottants

        Posté par  . Évalué à 2.

        Par contre, concernant les arrondis, l'utilisation de fonction explicite ou des mode de fpu influe sur quoi ? Uniquement la vitesse ? Ou il existe encore des subtilités ?

        Ca influe principalement sur trois choses : (en théorie)
        - La vitesse (ie le nombre de cycles nécessaires à une opération)
        - Le groupement des opérations. Par exemple supposons une opération trigonométrique. En FPU classique disons qu'elle va prendre 6 cycles, par contre pendant toute son execution elle va bloquer le pipeline. Donc c'est maximum 2 opérations (pairage pentium) par block de 6 cycles, et encore à condition de les lancer en même temps. La même interruption vectorisée va prendre 10 cycles mais elle ne bloquera le pipeline que si on change de type d'opération, dans le cas contraire on pourra recharger des valeurs deux cycles plus tard. Donc on aura une réponse au bout de 10 cycles, une seconde au bout de 12, une troisième au bout de 14 etc. Soit un cout degressif qui va tendre vers 2 cycles par opérations (contre trois en FPU classique). Par contre ce type d'opérations est très sensible aux interruptions.
        - Les ressources. La FPU classique tend à bouffer quelques registres éventuellement doublés, éventuellement étendus en interne et de la pile. Les opérations vectorielles elles bouffent des regsitres à la pelle, mais ne touchent pas trop à pile (en théorie toujours)

        En pratique, la différence entre ce qui devrait se passer en théorie d'après le code machine "en direct de l'assembleur" et se qui se passe réellement au niveau microcode fait que tout celà est de moins en moins vrai. Les processeurs modernes sont capables dans une certaine mesure d'aller piocher dans les regsitres de l'unité d'à coté quand celles-ci (les unités de traitement) ne sont pas tout simplement fusionnées au niveau hard et émulent un comportement ou l'autre en fonction du code machine. (à ce niveau là les processeurs des cartes graphiques sont un vrai bonheur). Bref pour optimiser correctement, il faut la doc complète du processeur (quand elle existe pour le grand public bien sur)

        Les modes fast sont souvent des optimisations et des réglages faits au niveau logiciel. Il y a en grosso modo de trois sortes :
        - désactivation de certains flags du CPU. (genre la carry sur off, le NAN qui devient une sentinelle et j'en passe)
        - réquisition de registres "pas fait pour" dans certains calculs. Ca permet d'avoir plus de registres et donc d'accélerer les calculs, mais certains évènements ne se déclenchent pas lorsqu'il y a des débordements par exemple, ou des nombres qui sont furieusement proches de 0
        - réorganisation des opérations dans un ordre qui est plus favorable au processeur (du coup on ne peut plus additionner des fourmis et des éléphants)
        Le fast math c'est la soupe du compilo.
        Les modes dit rapides sur les processeurs sont souvent des modes qui substituent ou ajoutent aux registres possédants une précision interne plus forte que la précision apparente des registres possédant une précision exacte. Mais on trouve aussi des modes rapides qui font des calculs complètement différents en fonction du mode choisi. C'est le cas par exemple des directives OpenGL de GLHint qui peuvent changer le comportement (et le rendu) du tout au tout en fonction de si on est en GL_NICEST ou en GL_FASTESt

        cela n'est génant que pour la detection d'errreur et non pour le fonctionnement nominal

        Oui et non. Quand on s'ampute du NAN il vaut mieux savoir ce que l'on fait. car on a plus aucun moyen de savoir si l'on a saturé l'espace de retour (le ou les regsitres qui contiennent le résultat), si l'on a passé des arguments incohérents ou si tout va bien. Bref il faut avoir borné à priori l'intervalle de des paramètres et celui du résultat. Si on décide, par exemple, pour gagner du temps d'utiliser des fonctions logiques (AND, OR etc.) sur des regsitres qui sont supposés être en FPU faut pas venir se plaindre après si les résultats ne veulent pas dire grand chose....
  • # Sans doute sans intérêt

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

    mais j'avais trouvé quelques infos intéressantes sur cet article:
    http://spooky-possum.org/cgi-bin/pyblosxom.cgi/x86fp.html
    Effectivement, un NaN ralentit d'un facteur 50 le calcul...
  • # Calcul par intervalle

    Posté par  . Évalué à 2.

    Comme signalé dans un commentaire précédent, pour du calcul rigoureux il existe des bibliothèques de calcule par intervalle, genre


    [1.5,1.6]+[0.1,0.2] = [1.6,1.8]

    qui permettent d'enclore rigoureusement les erreurs d'arrondi sur les calculs.


    Ce genre de calcul est utilisé dans la résolution de contraintes numériques par intervalles.
    • [^] # Re: Calcul par intervalle

      Posté par  . Évalué à 2.

      Cela marche toujours cette approche ? On ne risque pas de tomber rapidement dans un [-infini;+infini] ?

      J'imagine que cela peut arriver vite dans un 1/(a-b), avec a et b proche.

      "La première sécurité est la liberté"

      • [^] # Re: Calcul par intervalle

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

        Dans la publication suivante, les auteurs de MPFI expliquent que si l'intervalle grossit trop, ils refont le calcul avec une précision supérieure (précision pour l'intervalle du résultat). Genre on calcule a + b avec une précision de 10 chiffres, si lle résultat est trop gros, on calcule avec 20 chiffres.
        http://www.cs.utep.edu/interval-comp/interval.02/revo.pdf

        Ceci n'est possible qu'en utilisant une bibliothèque (MPFR) offrant une précision variable. Les flottants IEEE ont une précision fixe (32, 64, 80 bits).

        Le papier explique également que MPFI marche parce que MPFR réalise un arrondi exact : calculer 1 - 9e-10 avec une précision de 4 chiffres donne 0,999 et non pas 1,000 comme le fait Mapple IntPack (c'est un exemple simplifié pour illustrer le problème d'arrondi)... Si j'ai bien compris IntPack utilise les nombres IEEE et pas une bibliothèque du type MPFR à précision variable justement.

        Extrait du papier : « It can be noticed that the rule of thumb “to get more digits, one has to increase the computing precision by roughly the same number of digits” can fail, for instance when computing a square root or more generally a 1/n-th power close to 0. However, the rule of thumb becomes in such cases “to get α more digits, one has to increase the computing precision by roughly nα digits”. In other words, in most cases an increase in the computing precision yields an improved accuracy on the results. »
        • [^] # Re: Calcul par intervalle

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

          nicO se demandait pourquoi faut il de la précision et quel est l'impact de la précision. Hé bien durant mes études j'avais écrit un algorithme pour trouver les zéros de polynômes de n'importe quel degré. Le but étant d'intégrer des fractions rationelles (j'étais jeune et fou).
          http://www.haypocalc.com/wiki/Projet_AC20

          Et bien à partir de polynômes du 10e degré, je commençais à bien sentir les erreurs d'arrondi, l'algorithme ne fonctionnait plus. À force d'enchaîner les opérations, on perd petit à petit en précision pour finalement être totalement imprécis. À partir du 20e degré (je me souviens plus bien), l'algo marchait plus du tout.
          • [^] # Re: Calcul par intervalle

            Posté par  . Évalué à 2.

            La question général, c'est plus sur la méthode actuellement utilisé pour avoir un code correct sachant la difficulté de départ.

            Le but est évidement de rafiner tout ça pour trouver un modèle propre pour Lisaac.

            "La première sécurité est la liberté"

            • [^] # Re: Calcul par intervalle

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

              Bah pour un compilateur, le mieux est (comme GCC le fait, sauf si --ffast-math est utilisé) de ne pas chercher à « optimiser ». C'est au programmeur d'ordonner ses opérations.
              • [^] # Re: Calcul par intervalle

                Posté par  . Évalué à 2.

                Si on fournis au codeur un moyen d'estimer la précision, et/ou d'optimiser en lui garantissant la précision qu'il demande, je ne pense pas qu'il nous en veulent beaucoup...

                "La première sécurité est la liberté"

      • [^] # Re: Calcul par intervalle

        Posté par  . Évalué à 2.

        Tu peux améliorer la précision en faisant de la "propagation de contrainte" dans ces cas là, mais ça coûte, c'est un peu hors topic.

        De toute façon dans un cas pareil, la qualité de ton calcul a des chances de laisser à désirer au final.

Suivre le flux des commentaires

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