IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Perte de precision sur un float


Sujet :

C++

  1. #1
    Membre émérite
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    852
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 852
    Points : 2 298
    Points
    2 298
    Par défaut Perte de precision sur un float
    Bonsoir,

    J'ai rencontre un probleme que j'ai resolu mais j'aimerais comprendre ce qu'il s'est passe. Voici donc mon code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    float m_fps = float(sdl->updateScreen()); // retourne un Uint32
    std::cout << m_fps / 1000.f << std::endl;
    m_fps /= 1000.f;
    std::cout << m_fps << std::endl;
    updateScreen retourne une valeur comprise entre 15 et 30 (le temps qu'il faut pour dessiner une frame en ms). Du coup, pourquoi m_fps / 1000.f garde la precision alors que m_fps /= 1000.f ne la garde pas ? Je suis assez perplexe devant ce resultat donc si quelqu'un a une explication, je lui en serais tres reconnaissant.

    Merci d'avance !

  2. #2
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Parce que m_float n'est pas m_fps...

  3. #3
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Bonjour

    Parce que dans la machine les opérations se font en sur une précision supérieure à la taille des registres mais lorsque tu fais une affectation, tu forces l'écriture dans un registre (?) (À confirmer et donner une explication matérielle courante de nos jour MMX SSE)

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

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

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    Etape 1 : Lire http://floating-point-gui.de/

    Etape 2 : Lire plus précisement http://floating-point-gui.de/errors/rounding/

    L'affectation ou non ne change rien, la plupart des compilos vont garder les choses dans un registre.

    Ensuite, par contre, le passage de parametres a cout dans le cas cout << m_fps/1000.f evalue le temporarie d'une certaine manière dans un certain type de registre
    (je suppose un registre classqiue) potentiellement different du m_fps /= 1000.f qui doit passer par le jeu de registres xmm plutot que par la pile flottante.

    Un petit coup de objdump nous fixerais. Tu compiles en -O combien ?

  5. #5
    Membre émérite
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    852
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 852
    Points : 2 298
    Points
    2 298
    Par défaut
    Citation Envoyé par jo_link_noir Voir le message
    Parce que m_float n'est pas m_fps...
    Erreur de ma part lors de la copie du code, c'est modifie !

    Citation Envoyé par Joel J
    Merci pour tes liens, je vais voir si j'y trouve la reponse.

    Citation Envoyé par Joel J
    Un petit coup de objdump nous fixerais. Tu compiles en -O combien ?
    Je compile en -g2, pas de -O, je le garde pour la release (je ferai du -O2). Tu veux quelle(s) info(s) fournies par objdump ?

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

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

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    je veux la sortie du desassemblage complet

  7. #7
    Membre émérite
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    852
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 852
    Points : 2 298
    Points
    2 298
    Par défaut
    Ca fait quand meme 79 Mo... Tu veux pas un truc plus precis ?

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

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

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    le code du symbole qui contient ton code

  9. #9
    Membre émérite
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    852
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 852
    Points : 2 298
    Points
    2 298
    Par défaut
    J'ai trouve l'erreur et c'est juste bidon... Je stockais le resultat dans un Uint32 et non un float comme je le pensais... J'ai honte...

    @Joel F: Merci pour les liens en tout cas !

  10. #10
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par Joel F Voir le message
    L'affectation ou non ne change rien, la plupart des compilos vont garder les choses dans un registre.
    S'ils le peuvent. Pour les codes qui ont peu de variables oui, mais pas pour d'autres codes.
    J'avais eu le souci dans un espèce de jeu vidéo où les personnages se déplaçaient sur des cases mais leur déplacement se faisait en flottant. Et grosso modo faire le test du déplacement (pour savoir si la case n'était pas déjà occupée) et enregistrer le déplacement dans une donnée membre ne donnait pas le même résultat. Le "bug" était présent en -O2 mais pas en -O0. Et le "bug" n'était pas présent si je changeait la vitesse de déplacement de 0.2 par 0.5. J'avais passé des heures là dessus à l'époque :E

  11. #11
    Membre émérite
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    852
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 852
    Points : 2 298
    Points
    2 298
    Par défaut
    @Ehonn: J'avais eu un probleme similaire (par similaire j'entends sur les float) mais c'etait a cause de l'option -O3 alors que le bug disparaissait quand je passais en -O2. La magie des options d'optimisation...

  12. #12
    Membre expérimenté
    Avatar de Luke spywoker
    Homme Profil pro
    Etudiant informatique autodidacte
    Inscrit en
    Juin 2010
    Messages
    1 077
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant informatique autodidacte

    Informations forums :
    Inscription : Juin 2010
    Messages : 1 077
    Points : 1 742
    Points
    1 742
    Par défaut Complément d'information hors sujet.
    Salut les C++,

    Je connais que le C et j'ai eu un problème en rapport avec le sujet:

    Ont ne peut pas utiliser la fonction strtold() (string to long double) avec GTK+3.0 parce que GTK met la localisation avec la fonction setlocale() donc strtold() est inutilisable comme dit dans dans la manpage. Certes la glib fournit une fonction g_strtod() donc une précision double mais cela ne me convenait pas.

    J'ai donc fait une fonction maison:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <string.h>
    #include <inttypes.h>
     
    long double string_to_long_double(char *input) ;
     
    int main(int argc, char *argv[]) {
      if (argc != 2 || strcmp(argv[1],"-h") == 0 ) {
        fprintf(stdout,"usage: %s float_value\n",argv[0]) ;
        exit(EXIT_FAILURE) ;
      }
     
      long double res ;
     
      res=string_to_long_double(argv[1]) ;
     
      fprintf(stdout,"Entry: %s output: %.19Lf\n",argv[1],res) ;
     
      exit(EXIT_SUCCESS) ;
    }
     
    long double string_to_long_double(char *input) {
     
     
      long double res_tmp ;
      long double res_rounded ;
     
      long double cc ;
     
      int8_t c ;
     
      long double fp_exp ;
     
      char *ptr_float_point ;
     
      _Bool is_negativ = false ;
     
      char *integer_part_str=calloc(32,sizeof(char)) ;
      char *float_part_str=calloc(32,sizeof(char)) ;
      char *res_string=calloc(32,sizeof(char)) ;
     
     
      if (input[0] == '-') {
        input++ ;
        is_negativ=true ;
      }
     
     
     
     
     
     
      strcpy(integer_part_str,input) ;
      ptr_float_point=strchr(input,'.') ;
      integer_part_str[ptr_float_point-input]='\0' ; /** Get only integer part of the float */ 
     
     
      res_tmp = 0.0 ;
     
      strcpy(float_part_str,input) ;
     
      for (c=(int) strlen(integer_part_str)-1, cc=1 ; c >= 0 ; c--,cc *= 10) {
        res_tmp += ((int) (integer_part_str[c] - 48) * cc) ; /** Increment integer part */
     
        float_part_str++ ;    /** for getting an pointer only on the float part. */
     
        if (c == 0) {
          float_part_str++ ;  /** for getting an pointer only on the float part. */ 
          break ;
        }
     
      }
     
     
      for (c=0, fp_exp=0.1; c < (int) strlen(float_part_str) ; c++, fp_exp /= 10.0 ) {
        char *exponent_str=calloc(32,sizeof(char)) ;
     
        fprintf(stdout,"float multiplicator not cleaned -> %.19Lf\n",fp_exp) ;
     
        /** Clean fp_exp value method */
        sprintf(exponent_str,"%.*Lf",c+1,fp_exp) ;
        sscanf(exponent_str,"%Lf",&fp_exp) ;
     
     
        res_tmp += ((int) (float_part_str[c] - 48) * fp_exp) ; /** Increment float part */
     
        fprintf(stdout,"float multiplicator cleaned     -> %.19Lf\n",fp_exp) ;
     
        free(exponent_str) ;
      }
     
      /** Final result with the right rounding as the given string */
      sprintf(res_string,"%.*Lf",(int) strlen(float_part_str),res_tmp) ;
      sscanf(res_string,"%Lf",&res_rounded) ;
     
      for (c=(int) strlen(integer_part_str)-1 ; c >= 0 ; c--) { 
        /** Reset the right address for freeing pointer */
        float_part_str-- ;
        if (c == 0) {
          float_part_str-- ; 
          break ;
        }
      }
     
     
     
      free(integer_part_str) ;
      free(float_part_str) ;
      free(res_string) ;
     
      if (is_negativ) {
        res_rounded = -res_rounded ;
        input-- ;
      }
     
      fprintf(stdout,"Result -> %.19Lf\n",res_rounded) ;
     
      return res_rounded ; /** But the returned value is dirty too... */
    }
    Mais la valeur n'est jamais exacte en arrondissant a .19Lf -> il y a du brouillage dans les petites valeurs dans la part après la virgule (le point).

    Il faut savoir que:

    type float précision 6 maximale codé sur 4 bytes.
    type double précision 15 maximale codé sur 8 bytes.
    type long double précision 19 maximale codé sur 10 bytes.

    Je pense que le brouillage vient du codage interne des float, double, long double sous forme de:
    mantisse, exposant, signe.
    Le nombre de bits attribuer a la mantisse et a l'exposant dépendent du type et un bit pour le signe (d'ailleurs je crois que le 10 ième byte du long double ne sert que a stocker le signe).
    L'ordinateur ne peut pas stocker les valeurs exactes selon ce système de codage, mais des valeurs très proches.

    La FPU (Floating-Point Unit) qui sert (servait) a stocker les nombre a virgule est composé de 8 registre de de 80 bits (10 bytes) sur un ordinateur 32 bits.

    Mais actuellement ont joue avec des registres XMM (128 bits) ou YMM (256 bits) sur les machines 64 bits.

    Ont peut stocker plusieurs nombres a virgules a la fois dans un registre:

    -) 4 floats (simple précision.) XMM.
    -) 2 double (double précision.) XMM.

    Et effectuer des opérations simultanément sur plusieurs nombres (Simple Instruction Multiple Data) dans ces registres.

    Bon si je n'ai pas été utile j'ai été au moins hors sujet.

    PS: La précision dépend aussi de la taille de la partie entière.
    Pour faire tes armes:
    Use du présent pour construire ton futur sinon use de ce que tu as appris auparavant.
    Et sois toujours bien armé avant de te lancer.
    Le hasard ne sourit qu'aux gens préparés...
    Site: Website programmation international (www.open-source-projects.net)
    Site: Website imagerie 3D (www.3dreaming-imaging.net)
    Testez aux moins pendant une semaine l'éditeur avec terminaux intégrées it-edit Vous l'adopterai sûrement !
    FUN is HARD WORK !!!

  13. #13
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par Joel F Voir le message
    L'affectation ou non ne change rien, la plupart des compilos vont garder les choses dans un registre.
    Les calculs intermédiaires peuvent se faire avec une précision supérieure, une assignation doit ramener le résultat à la précision de la variable. GCC a longtemps eu des problèmes avec ça (voir https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323)
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Axis precision sur les Float et les doubles pour le fun
    Par pcouas dans le forum Services Web
    Réponses: 0
    Dernier message: 29/08/2009, 13h40
  2. [Math]probleme de precision de calcul sur les float
    Par calvin dans le forum Langage
    Réponses: 6
    Dernier message: 26/05/2005, 07h53
  3. besoin de precision sur gluLookAt!
    Par bakonu dans le forum OpenGL
    Réponses: 3
    Dernier message: 25/04/2004, 19h05
  4. precision sur le pilotage du port parallele
    Par fransouik dans le forum C++Builder
    Réponses: 18
    Dernier message: 26/02/2004, 13h28
  5. [VBA-E]Demande de précision sur les menus
    Par geffdenimes dans le forum Macros et VBA Excel
    Réponses: 5
    Dernier message: 25/06/2003, 10h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo