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 :

problème avec l'utilisation de atof()


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Janvier 2013
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 51
    Points : 34
    Points
    34
    Par défaut problème avec l'utilisation de atof()
    Bonjour,

    je rencontre un problème qui doit revenir assez souvent mais pour lequel je ne trouve pas de solution simple. Je lis cette chaîne de caractère dans une fichier "4201746.4582", il se trouve que c'est un nombre. Voici la ligne de code qui effectue la lecture dans mon programme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char *X = strtok( NULL, " " );
    que je transforme ensuite en flottant avec or lorsque j'écris ma variable dans un fichier text, ce qui est enregistré est "4.20175e+06".

    ce que j'aimerai c'est d'écrire la même chose que ce que je lis ...

    merci d'avance de votre aide.

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Le problème n'est pas la fonction atof (qui, au passage, ne devrait pas plus être utilisée qu'un char * en C++ ) mais bien la représentation "textuelle" que tu en récupères, auquel il faut rajouter le problème de l'imprécision de la conversion des nombres à virgules flottantes.

    Je m'explique :

    Tout n'est en réalité qu'une question de conventions. Pour un humain, les conventions d'écriture passent en générale par une chaine de caractères (ou plutôt une chaine de "glyphes" pour prendre en compte aussi bien les lettres que les chiffres ou les symboles particuliers), alors que, du coté de l'ordinateur, la convention qu'il utilise pour manipuler les différente valeurs est une représentation binaire (composée exclusivement d'une succession de 0 et de 1. Et encore, ce n'est là qu'une convention adaptée à l'humain, parce qu'elle permet de représenter le fait que le courent passe ou non sur une broche bien déterminée )

    La fonction atof -- ainsi que toutes les fonctions similaires -- va donc servir de "passerelle" qui permet de passer d'une convention donnée (dans le cas présent d'une convention "humainement lisible sous la forme de chaine de caractères) à une autre (dans le cas présent, à un type que l'ordinateur considérera comme étant un nombre à virgule flottante).

    Or, lorsque l'on travaille avec des nombres à virgule flottante, il y a "forcément" des valeurs qui ne peuvent pas être représentées sous une forme finie.

    Penses, par exemple, à la valeur de PI dont certains savent qu'elle correspond à 3.14, d'autres à 3.1415 et d'autres encore à 3.1415926 ou à la valeur de 1/3 qui vaut 0.3333...(indéfiniment 3). Mais le fait est qu'il y a encore "tout plein d'autres chiffres" après le dernier connu : on ne peut, simplement, pas décider de s'arrêter à N chiffres après la virgule, parce qu'il y aura toujours l'imprécision due au N+1eme chiffre envisagé.

    Il en va de même pour les ordinateurs.

    Pour un type de valeur à virgule flottante donné (par exemple le float), qui est de taille strictement fixe, l'ordinateur va en fait utiliser ce que l'on appelle la notation scientifique (XXX e YYY ou XXX est une valeur numérique correspondant à la mantisse et YYY est une valeur d'exposant (ce qui correspond à XXX * 10 exp YYY).

    La seule différence, c'est que les ordinateurs vont gérer les différentes valeurs sous la forme de bits (qui représentent des valeurs égales à 0 ou à 1).

    Du coup, quand tu converti 4201746.4582 en float, il va -- effectivement -- manipuler cette valeur sous la forme de 4.201 746 458 2 * 10 exposant 6 parce qu'il faut "décaler" la virgule de 6 chiffres vers la droite pour avoir la valeur réelle.

    Et, le problème qui peut se poser, vu qu'il y a conversion d'une convention de représentation vers une autre, c'est que la mantisse au niveau de l'ordinateur ne dispose que d'un nombre limité de bits et qu'il ne soit, tout simplement, pas possible de représenter la valeur 42 017 464 582. Il faudra donc "laisser tomber" les deux derniers chiffres, par exemple (en fait, ce n'est pas tout à fait vrai, parce que la valeur représentée sous sa forme binaire pourrait etre de 42 017 464 596 ou quelque chose du genre, mais le principe est là ) . En générale, le type float n'a en effet qu'une précision maximale de ... 7 chiffres après la virgule. Or, là on est déjà forcément à 11 chiffres représentatifs

    Lorsque, par la suite, tu vas demander d'écrire cette valeur, que ce soit dans un fichier ou sur la sortie standard, il y aura de nouveau une conversion parce que ce qui sera affiché, c'est une chaine de caractères représentant la valeur de ta variable sous une forme "lisible par l'humain".

    A ce moment là, le problème est, tout simplement, que les informations qui ont été "perdues" lors de la première conversion ne pourront absolument plus être récupérées.

    Et, lorsque la règle de conversion rencontrera ton float qui représente maintenant une valeur sans doute proche de 4.201 746 * 10 exposant 6, elle traduira cette valeur de la manière la plus simple qu'elle connaît, à savoir : la représentation sous la forme scientifique.

    Il y a donc deux problèmes distincts :
    Le premier est d'utiliser un type à virgule flottante qui permette de représenter correctement (comprends: sans perte de données trop importante) les valeurs que tu manipules.

    De toutes évidence, le type float est déjà "trop petit". Donc, tu devrais plutôt envisager le recours au type double (voir long double), dont la mantisse permet typiquement de représenter des valeurs avec une précision allant jusqu'à 14 chiffres après la virgule (En fait, ce n'est pas tout à fait vrai, parce que la norme est moins précise que cela, mais l'idée est de te faire comprendre le problème ici ).

    Évidemment, cela signifie que tu ne dois plus utiliser la fonction atof() mais bien la fonction qui te permettra de convertir des valeurs en double

    Le deuxième "problème" qu'il faudra résoudre sera de faire en sorte que la conversion t'affiche la valeur sous une forme "classique" (par opposition à la forme scientifique, vais-je écrire) de 4201746.4582.

    Pour y arriver, tu dois simplement "jouer" sur la taille des informations affichées. En C++, tu peux formater la sortie sous une forme qui serait, par exemple, proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::cout<<std::fixed<<monDouble;
    ou, si tu travailles sur un fichier, qui prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::ofstream ofs("fichier.txt");
    ofs<<std::fixed<<monDouble;
    (dans lesquelles monDouble serait, tu t'en seras douté, le nom de la variable de type double à afficher / écrire dans le fichier )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Janvier 2013
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 51
    Points : 34
    Points
    34
    Par défaut
    Bonjour,

    merci pour votre réponse et toutes vos explications.

    J'ai changé la ligne de lecture en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::string X = strtok( NULL, " " );
    et l'enregistrement en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    strtod(X.c_str(), NULL)
    et l'écriture en ajoutant le et cela marche comme je veux !

    j'ai toutefois une question. Pourquoi dites vous dans votre réponse :
    ... atof (qui, au passage, ne devrait pas plus être utilisée qu'un char * en C++ ...
    merci encore pour votre aide.

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par med.doc Voir le message
    Bonjour,

    merci pour votre réponse et toutes vos explications.

    J'ai changé la ligne de lecture en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::string X = strtok( NULL, " " );
    et l'enregistrement en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    strtod(X.c_str(), NULL)
    et l'écriture en ajoutant le et cela marche comme je veux !
    Oups, malheureux!!!

    Tu ne dois récupérer le résultat de tes fonction strtoXXX que dans une variable du type dans lequel tu demande la conversion (un double en l'occurrence).

    C'était d'ailleurs déjà le cas pour les fonctions atoXX!!!

    j'ai toutefois une question. Pourquoi dites vous dans votre réponse :
    ... atof (qui, au passage, ne devrait pas plus être utilisée qu'un char * en C++ ...
    merci encore pour votre aide.
    Parce que c'est toujours le même problème :
    C++ vient avec des solutions beaucoup plus sécurisées que ce que ne le seront jamais les solutions équivalentes issues du C.

    Manipuler des chaines de caractères à coup de char *, comme en C, t'obliges à systématiquement vérifier que l'espace mémoire alloué pour en permettre la représentation soit suffisant avec des conséquences catastrophiques si ce n'est pas le cas.

    La classe std::string évite ces problèmes en prenant tous les tenants et aboutissants en charge.

    De même, les fonctions atoXXX / strtoXXX ne peuvent fonctionner que si le char * transmis comme "donnée d'origine" correspond à une valeur susceptible d'être convertie (la chaine de caractères "azerty" n'a aucune chance d'être convertie ), bien qu'il me semble que strtoXX puisse lancer une exception si la conversion échoue.

    Pour les conversions, rien ne vaut (en termes de sécurité du moins) la classe stringstream qui est fournie dans l'espace de noms std, car elle permet de vérifier aisément si la conversion a eu lieu, et qu'elle permet en outre de simplifier la conversion de deux doubles issus d'une chaine de caractères proches de "4201746.4582 2854647.1024" que tu pourrais assez facilement obtenir dans un fichier dans lequel les valeurs correspondraient au coordonnées de points, par exemple.

    En un mot comme en 100, l'utilisation des possibilités issues du C++ sera toujours plus sécurisante à l'emploi que celle des possibilités issues du C

    C'est pour cela que l'on insiste généralement beaucoup pour que les gens utilisent des références (éventuellement constantes) au lieu de pointeurs et les fonctionnalités propres au C++ "chaque fois que faire se peut" .
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

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

Discussions similaires

  1. Problème avec l'utilisation de LogMessage
    Par vanquish dans le forum API, COM et SDKs
    Réponses: 3
    Dernier message: 17/11/2005, 10h18
  2. Problème avec l'utilisation d'un module
    Par goblin dans le forum Modules
    Réponses: 4
    Dernier message: 09/11/2005, 20h55
  3. Problème avec l'utilisation de librairies
    Par Aradesh dans le forum MFC
    Réponses: 3
    Dernier message: 01/08/2005, 15h00
  4. [debutant] problème avec type à utiliser
    Par mlequim dans le forum Autres SGBD
    Réponses: 2
    Dernier message: 15/07/2005, 16h08
  5. Problème avec l'utilisation de la fonction clock
    Par Matgic95 dans le forum C++Builder
    Réponses: 13
    Dernier message: 09/05/2005, 19h27

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