Comment printf et scanf poignée de précision en virgule flottante de formats?
Envisager l'extrait de code suivant:
float val1 = 214.20;
double val2 = 214.20;
printf("float : %f, %4.6f, %4.2f \n", val1, val1, val1);
printf("double: %f, %4.6f, %4.2f \n", val2, val2, val2);
Sorties:
float : 214.199997, 214.199997, 214.20 | <- the correct value I wanted
double: 214.200000, 214.200000, 214.20 |
Je comprends que 214.20
a une infinité de représentation binaire. Les deux premiers éléments de la première ligne ont une approximation de la valeur de référence, mais le dernier ne semble pas avoir de rapprochement à tous, et cela m'a conduit à la question suivante:
Comment les scanf
, fscanf
, printf
, fprintf
(etc.) les fonctions de traiter la précision des formats?
Sans précision fournie, printf
imprimé d'une valeur approximative, mais avec %4.2f
il a donné le bon résultat. Pouvez-vous m'expliquer l'algorithme utilisé par ces fonctions pour gérer précision?
Vous devez vous connecter pour publier un commentaire.
La chose est, 214.20 ne peut pas être exprimée exactement avec représentation binaire. Quelques nombres décimaux peuvent. Ainsi, un rapprochement est stocké. Lors de l'utilisation de printf, la représentation binaire est transformé en une représentation décimale, mais il ne peut pas être exprimé exactement et est seulement approximative.
Comme vous l'avez remarqué, vous pouvez donner une précision à printf dire comment faire pour arrondir les décimales rapprochement. Et si vous ne lui donnez pas une précision alors une précision de 6 est utilisé (voir la page de manuel pour plus de détails).
Si vous utilisez
%.40f
pour le flotteur et%.40lf
pour le double dans votre exemple ci-dessus, vous obtiendrez les résultats suivants:Ils sont différents parce que à double, il y a plus de bits afin de mieux reproduire les 214.20. Mais comme vous pouvez le voir, ils sont toujours très étrange lorsqu'il est représenté en décimal.
Je vous recommande de lire le Article de wikipédia sur les nombres à virgule flottante pour plus d'idées sur la façon dont les nombres à virgule flottante de travail. Une excellente lecture est également Ce Que Tout Informaticien Devez Savoir À Propos De L'Arithmétique À Virgule Flottante
%4.2f
vous donne exactement la valeur que vous avez voulu. C'est toujours "faux" puisque la représentation binaire est pas équivalent à 214.20 mais à 214.1999969482421875. Maintenant, si vous ronde "214.1999969482421875" à 6 chiffres, vous obtenez "214.199997" parce que le 7ème chiffre est de 9, tournant le 6 en 7. Mais quand vous avez seulement arrondir à 2 chiffres, vous obtenez "214.20" parce que le 3ème chiffre est un neuf, si vous avez besoin d'augmenter le 9 sur le 2ème chiffre. Qui "déborde" de 10, tournant le ".19" en ".20".Puisque vous m'avez demandé
scanf
, une chose que vous devez noter est que POSIX nécessiteprintf
et unescanf
(oustrtod
) afin de reconstruire la valeur d'origine exactement aussi longtemps que suffisamment de chiffres significatifs (au moinsDECIMAL_DIG
, je crois) ont été imprimés. Plaine C bien sûr, ne fait pas une telle exigence; la norme C assez bien permet d'opérations en virgule flottante pour donner ce résultat le réalisateur aime aussi longtemps que ils du document. Toutefois, si votre intention est de stocker des nombres à virgule flottante dans des fichiers texte à lire plus tard, vous pourriez être mieux à l'aide de la C99%a
spécificateur de les imprimer au format hexadécimal. De cette façon, ils vont être exact, et il n'y a pas de confusion quant à savoir si le sérialiser/désérialiser processus perd en précision.Garder à l'esprit thatr quand j'ai dit "reconstruire la valeur d'origine", je veux dire la valeur réelle qui a eu lieu dans la variable/expression passée à
printf
, pas l'original virgule vous a écrit dans le fichier source qui s'est arrondi à la meilleure représentation binaire par le compilateur.scanf
arrondir la valeur d'entrée la plus proche exactement représentable valeur à virgule flottante. Comme DarkDust réponse illustre, en simple précision la plus proche exactement représentable valeur est inférieure à la valeur exacte, et en double précision la plus proche exactement représentable valeur est au-dessus de la valeur exacte.