Author: Paul Gaborit Date: Jan 29, 2008 10:03
À (at) Tue, 29 Jan 2008 15:18:15 +0100,
"Julien K." wherever.you.want.com> écrivait (wrote):
> j'écris un script de lecture de données binaires produites par un code de
> calcul en F77; une valeur de charge particulière est affectée aux mailles
> inactives, typiqument 999.99. Les valeurs sont des REALs écrits en binaire
> non formatté.
>
> Malheureusement dans la lecture du fichier je ne récupère pas exactement
> cette valeur, je suis confronté à un problème de ce genre:
>
> $ perl -e 'print unpack("f",(pack("f",999.99))) ;'
> 999.989990234375
>
> $ perl -e 'print sprintf("%%.4f",unpack("f",(pack("f",999.99)))) ;'
> 999.9900
>
> Comment puis-je faire pour récupérer 999.99, valeur utilisée dans des
> tests? Utiliser sprintf résoud le problème mais ça ne me semble pas /propre/.
À mon avis, vous feriez mieux de travailler en DOUBLE plutôt qu'en
FLOAT. La quasi totalité des processeurs modernes ne savent plus
utiliser les floats (ils font leurs calculs en double et filtrent au
dernier moment pour simuler les floats). Il n'y a que lorsqu'on est
très très contraint en place mémoire qu'on utilise les floats mais
c'est quand même très rare.
Bon, ceci étant dit, vous n'avez peut-être pas le choix...
Le problème constaté est tout fait normal et n'a rien à voir avec Perl
ou perl. C'est juste que la valeur stockée par un float ne peut pas
être tout pile 999.99 donc si on l'affiche avec trop de précision on
voit les derniers bits erronés.
La solution du sprintf est pas mal pour arrondir mais le .4f est
arbitraire (pourquoi 4 décimales après la virguel, et si la valeur
vaut 1e-5 ou 1e20...). Je propose donc d'utiliser une même limite arbitraire
mais ne dépendant pas de l'ordre de la valeur :
perl -e 'print sprintf("%%g", unpack("f",(pack("f",999.99)))), "\n" ;'
|