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

Delphi Discussion :

[Delphi 6] probleme de precision avec StrToFloat()


Sujet :

Delphi

  1. #1
    Membre à l'essai
    Inscrit en
    Mars 2005
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 33
    Points : 20
    Points
    20
    Par défaut [Delphi 6] probleme de precision avec StrToFloat()
    Bonjour,

    j ai un probleme de conversion avec la fonction StrToFloat() sous Delphi 6. Peut-etre quelqu un aura-t-il une solution simple a proposer.

    J'ai une valeur numérique qui est saisie dans une StringGrid (par exemple, 0.001).
    Je souhaite la convertir en Double pour pouvoir la traiter comme une valeur numérique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    stepValue : Double;
    stepValue := StrToFloat(grid.Cells[3,1]);
    avec grid.Cells[3,1], la cellule contenant la valeur numerique.

    Le probleme est que stepValue ne va pas valoir "0.001" mais "0.001 plus ou moins un epsilon" (0.00999999999). Cette erreur de precision a des répercussion dramatique sur le reste de mon programme.

    Par exemple, si je fais floor(1/stepValue), resultat ne vaut pas 1000 mais 999

    Je pense que le probleme peut provenir du fait que StrToFloat fournit un 'extented' alors que ma variable est de type 'double'.

    Comment puis-je regler mon probleme de conversion 'string' -> 'double' si vous plait ? Comment eviter une perte de precision lors du passage 'string' vers 'double' ?

    Ludo

  2. #2
    Membre expert
    Avatar de TicTacToe
    Inscrit en
    Septembre 2005
    Messages
    1 940
    Détails du profil
    Informations personnelles :
    Âge : 51

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 940
    Points : 3 575
    Points
    3 575
    Par défaut
    Le problème des flottants est toujours omniprésent, et ne stocke pas forcément la réalité du nombre visuel (qui est ou non un arrondi).

    Dans ce cas, utilise un extended et non pas un double, qui permet d'avoir une plus grande précision, mais le problème des arrondis subistera sur certaines valeurs (on tombe ou non dessus...)

    Si tu veux être absolument certains des valeurs, et si tes nombres sont dans une fourchette raisonnable, tu peux avoir 2 solutions:
    Utiliser des Currency (la virgule est fixe, ce ne sont pas des flottants),
    ou alors des entiers que tu multiplies/divisent par un certains coef selon la précision souhaitée.

    Ceci dit, avec ton exemple 0.001, double ou extended donnent strictement le bon résultat en essayant FloatToStr.
    J'ai testé ce code:

    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
    procedure TForm1.Button1Click(Sender: TObject);
    var
       d: double;
    begin
         d := StrToFloat( Edit1.Text );
         Label1.caption := FloatToStr( d );
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
       e: extended;
    begin
         e := StrToFloat( Edit1.Text );
         Label2.caption := FloatToStr( e );
    end;
    as tu un nombre particulier qui pose problème ?

  3. #3
    Membre confirmé
    Avatar de Philippe Gormand
    Inscrit en
    Mars 2002
    Messages
    330
    Détails du profil
    Informations forums :
    Inscription : Mars 2002
    Messages : 330
    Points : 647
    Points
    647
    Par défaut
    Bonjour.

    Tu peux utiliser la procédure "Val" qui (à ma connaissance) n'a pas le problème de la fonction "StrToFloat".

    A+

  4. #4
    Membre à l'essai
    Inscrit en
    Mars 2005
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 33
    Points : 20
    Points
    20
    Par défaut
    Salut,

    Merci pour les réponses

    @Phil
    La fonction 'val' ne corrige malheureusement pas le probleme.

    @TicTacToe
    Ton bout de code marche, mais comme tu le disais, ce qui est affiché n'est pas forcement la valeur réel. Comme dans mon cas, lorsque j affiche mes variables, aucun probleme en apparence.
    Tu peux essayer Floor(1/d) avec 0.001. J obtiens 999 au lieu de 1000. C est tres tres genant dans mon cas car mon algo fait 999 iterations au lieu des 1000 prevues

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    488
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 488
    Points : 397
    Points
    397
    Par défaut
    Citation Envoyé par Philippe Gormand
    Tu peux utiliser la procédure "Val" qui (à ma connaissance) n'a pas le problème de la fonction "StrToFloat".
    Non, non, comme l'a dit TicTacToe le problème est plus profond : Il n'existe pas de représentation exacte de 0.001 en double, aucune fonction de conversion ne pourra rien y faire. Par contre beaucoup de composants permettent de limiter le nombre de chiffres après la virgule qui sont affichés.

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    488
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 488
    Points : 397
    Points
    397
    Par défaut
    Citation Envoyé par ludovic tambour
    Tu peux essayer Floor(1/d) avec 0.001. J obtiens 999 au lieu de 1000. C est tres tres genant dans mon cas car mon algo fait 999 iterations au lieu des 1000 prevues
    Tu peux utiliser Round(1/d) qui lui donnera 1000. Je ne sais pas si ça répond à ton problème.

  7. #7
    Membre à l'essai
    Inscrit en
    Mars 2005
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 33
    Points : 20
    Points
    20
    Par défaut
    @TicTaeToe

    Le probleme est visible en inserant dans ton code : d := Floor(1/d);

    idem avec les extended.

    Si tu saisie 0.001, tu as un probleme avec des double, mais aucun probleme avec des extended.

    Je vais etre obligé de passer mes variables en Extended. A vrai dire, cette solution me convient a moitié. D apres doc sur Delphi, le type Extended est moins portable que le type Double. C est pour cela que je preferai rester en double.

    Ludo

  8. #8
    Membre à l'essai
    Inscrit en
    Mars 2005
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 33
    Points : 20
    Points
    20
    Par défaut
    @sovitec

    L'utilisation de 'round' n ira pas dans mon probleme

    A vrai dire, j ai 2 choses a faire où ce probleme de précision pose problème.

    1) Le premier problème est de faire évoluer une variable 'currentValue' depuis 'minValue' jusqu'à 'maxValue' avec un pas de 'stepValue'. 'minValue', 'maxValue' et 'stepValue' sont saisies initialement comme des String puis convertit en double via StrToFloat. Si minValue = '499', maxValue = '500' et stepValue = '0.001', du fait du probleme de precision, 'stepValue' peut etre interprété comme valant '0.001000...001'. Donc, a la derniere iteration, currentValue ne vaudra pas 500 mais 499

    2) Determiné le nombre d'iteration qui sera realisé pas (1). La formule est : floor((maxValue-minValue)/stepValue) + 1;

    Comme expliqué precedement, j aurais un probleme si '0.001' est interprété comme valant '0.009999999....'

    Encore merci pour vos reponse

    ludo

  9. #9
    Membre expert
    Avatar de TicTacToe
    Inscrit en
    Septembre 2005
    Messages
    1 940
    Détails du profil
    Informations personnelles :
    Âge : 51

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 940
    Points : 3 575
    Points
    3 575
    Par défaut
    1ere remarque sur le code suivant:

    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
    procedure TForm1.Button1Click(Sender: TObject);
    var
       d: double;
    begin
         d := StrToFloat( Edit1.Text );
         Label1.caption := FloatToStr( Floor( 1 / d ) );
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
       e: extended;
    begin
         e := StrToFloat( Edit1.Text );
         Label2.caption := FloatToStr( Floor( 1/ e ) );
    end;
    le résultat est bien 999 pour le cas 'Double', et 1000 pour le cas 'Extended'.
    Donc Extended résoud bien la pb pour ce cas précis, mais tu t'exposes à des problèmes futurs.

    2eme Remarques, pourquoi ne pas faire:
    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
    procedure TForm1.Button1Click(Sender: TObject);
    var
       d: double;
    begin
         d := StrToFloat( Edit1.Text );
         Label1.caption := FloatToStr( Round( 1 / d ) );
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
       e: extended;
    begin
         e := StrToFloat( Edit1.Text );
         Label2.caption := FloatToStr( Round( 1/ e ) );
    end;
    Le résultat final de la division sera tout le temps "proche" de 1000.
    Par contre, le défaut avec Floor, c'est que si le Delta est négatif, cela renverra bel est bien 999
    -> Pas avec Round

    Si ce n'est pas satisfaisant, je ne vois pas d'autres alternatives à utiliser un coefficient intermédiaire, pour stocker tes nombres d'une manière sures.

    -> C'est d'ailleurs le cas des Currency, qui sont en fait des entiers en interne
    -> c'est une méthodes qui était utilisée il y a longtemps lorsque les opérations flottantes prenaient bcp de cycles cpu, ou tout était stocker en entier meme si en entrée/sortie du nombre cela etait convertis en réels visuels.

    Le currency je crois a une précision de 5 chiffres apres la virgule. Et 0.001 sera toujours pile poil 0.001, par contre attention aux opérations intermédaires qui interagissent avec les flottants...

  10. #10
    Membre à l'essai
    Inscrit en
    Mars 2005
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 33
    Points : 20
    Points
    20
    Par défaut
    Tout a fait d accord avec toi TicTacToe,

    'Extended' resoud mon probleme pour 0.001 mais :
    a) Peut-etre qu il y a une autre valeur qui va deconner aussi avec 'Extended'
    b) Probleme de portabilité potentiel

    Pourquoi, j utilise un 'floor' et pas un 'round' -> cf point 2 de mon message precedent. La formule qui me donne le nombre d iteration est avec un 'floor' et deconne avec un 'round'

  11. #11
    Membre à l'essai
    Inscrit en
    Mars 2005
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 33
    Points : 20
    Points
    20
    Par défaut
    Si ce n'est pas satisfaisant, je ne vois pas d'autres alternatives à utiliser un coefficient intermédiaire, pour stocker tes nombres d'une manière sures.
    Qu est ce que tu entend par "coefficient intermediaire" stp ?

  12. #12
    Membre expert
    Avatar de TicTacToe
    Inscrit en
    Septembre 2005
    Messages
    1 940
    Détails du profil
    Informations personnelles :
    Âge : 51

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 940
    Points : 3 575
    Points
    3 575
    Par défaut
    Dans ton cas (mais je connais pas le contexte),
    tu peux utiliser que des entiers...
    500 devient 500 000 en interne
    0.001 devient 1 en interne
    etc...

    Et la tu effectues toutes tes opérations avec des entiers.

    -> ce qui implique de décoder toi meme le "0.001"
    -> par exemple Round( StrToFloat( MaChaineFloat ) * 1000 )
    -> ou alors, encore plus fiable, tu décodes toi meme la chaine, en y intégrant dans ta fonction ton coef mille (ex: tu déplaces virtuellement la virgule de 3 rangs vers la droite), puis tu Round, puis ru fais un StrToInt.

    -> ce qui implique qu'en sortie visuelle, tu fasses l'opération inverse.

    M'enfin, les Currency te feront peut être suffisants... as-tu essayé de remplacer tes Double par des Currency ?

  13. #13
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    488
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 488
    Points : 397
    Points
    397
    Par défaut
    Sinon une proposition (c'est surement bête, mais on ne sait jamais) : Tu pourrais demander le nombre de pas plutôt que le pas lui même.

Discussions similaires

  1. Probleme d'enregistrement avec delphi
    Par scofield1 dans le forum Bases de données
    Réponses: 5
    Dernier message: 12/09/2011, 18h38
  2. [Delphi 4] Probleme sur Blob null avec UpdateSQL
    Par alakauf dans le forum Bases de données
    Réponses: 4
    Dernier message: 25/05/2010, 21h15
  3. probleme d'affichage avec delphi 2009
    Par dz_bill dans le forum EDI
    Réponses: 1
    Dernier message: 19/08/2009, 11h49
  4. [Delphi] [DLL] Problème avec un paramètre PChar
    Par Mickey.jet dans le forum Langage
    Réponses: 1
    Dernier message: 22/03/2006, 16h43
  5. probleme avec strtofloat
    Par copeau31 dans le forum Langage
    Réponses: 6
    Dernier message: 20/01/2006, 19h24

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