Existe-t-il un printf
spécificateur de largeur qui peut être appliqué à un spécificateur à virgule flottante qui formaterait automatiquement la sortie avec le nombre nécessaire de chiffres significatifs sorte que lors du balayage de la chaîne, la valeur à virgule flottante d'origine soit acquise?
Par exemple, supposons que j'imprime a float
avec une précision de 2
décimales:
float foobar = 0.9375;
printf("%.2f", foobar); // prints out 0.94
Lorsque je scanne la sortie 0.94
, je n'ai aucune garantie conforme aux normes que j'obtiendrai l'original0.9375
virgule flottante d' (dans cet exemple, je ne le ferai probablement pas).
Je voudrais un moyen printf
d'imprimer automatiquement la valeur à virgule flottante sur le nombre nécessaire de chiffres significatifs pour s'assurer qu'elle peut être numérisée à la valeur d'origine transmise àprintf
.
Je pourrais utiliser certaines des macros float.h
pour dériver la largeur maximale à laquelle passer printf
, mais existe-t-il déjà un spécificateur pour imprimer automatiquement le nombre nécessaire de chiffres significatifs - ou du moins jusqu'à la largeur maximale?
la source
printf( "%f", val );
ce qui est déjà portable, efficace et par défaut.double
. Comme votredouble
devient extrêmement grand (très loin de 1.0), il devient en fait moins précis dans la partie décimale (partie de valeur inférieure à 1.0). Donc, vous ne pouvez pas vraiment avoir une réponse satisfaisante ici, car votre question contient une fausse hypothèse (à savoir que tous lesfloat
s /double
s sont créés égaux)Réponses:
Je recommande la solution hexadécimale @Jens Gustedt: utilisez% a.
OP veut «imprimer avec une précision maximale (ou au moins jusqu'à la décimale la plus significative)».
Un exemple simple serait d'imprimer un septième comme dans:
Mais creusons plus profondément ...
Mathématiquement, la réponse est "0,142857 142857 142857 ...", mais nous utilisons des nombres à virgule flottante de précision finie. Supposons que le binaire double précision IEEE 754 . Donc, les
OneSeventh = 1.0/7.0
résultats dans la valeur ci-dessous. Lesdouble
nombres à virgule flottante représentables précédents et suivants sont également représentés .L'impression de la représentation décimale exacte de a
double
a des utilisations limitées.C a 2 familles de macros
<float.h>
pour nous aider.Le premier ensemble est le nombre de chiffres significatifs à imprimer dans une chaîne en décimal, donc lors de la numérisation de la chaîne, nous obtenons la virgule flottante d'origine. Ils sont affichés avec la valeur minimale de la spécification C et un exemple de compilateur C11.
Le deuxième ensemble est le nombre de chiffres significatifs qu'une chaîne peut être balayée en virgule flottante, puis le FP imprimé, en conservant toujours la même présentation de chaîne. Ils sont affichés avec la valeur minimale de la spécification C et un exemple de compilateur C11. Je crois disponible avant C99.
La première série de macros semble atteindre l'objectif de OP significatif chiffres . Mais cette macro n'est pas toujours disponible.
Le "+ 3" était au cœur de ma réponse précédente. Son centré sur la connaissance de la conversion aller-retour string-FP-string (set # 2 macros disponibles C89), comment déterminer les chiffres pour FP-string-FP (set # 1 macros disponibles après C89)? En général, ajouter 3 était le résultat.
Maintenant combien chiffres significatifs à imprimer est connu et piloté
<float.h>
.Pour imprimer N chiffres décimaux significatifs, on peut utiliser différents formats.
Avec
"%e"
, le champ de précision correspond au nombre de chiffres après le chiffre principal et la virgule décimale. Il en- 1
va de même. Remarque: ce-1
n'est pas dans leint Digs = DECIMAL_DIG;
Avec
"%f"
, le champ de précision correspond au nombre de chiffres après la virgule décimale. Pour un nombre commeOneSeventh/1000000.0
, il faudraitOP_DBL_Digs + 6
voir tous les chiffres significatifs .Remarque: Beaucoup sont habitués à
"%f"
. Cela affiche 6 chiffres après la virgule décimale; 6 est la valeur d'affichage par défaut, pas la précision du nombre.la source
%f
est de 6."%f"
en premier lieu. Utiliser"%e"
comme vous l'avez montré est bien sûr une meilleure approche globale et effectivement une réponse décente (bien que ce ne soit peut-être pas aussi bon que l'utilisation"%a"
pourrait l'être s'il est disponible, et bien sûr"%a"
devrait être disponible si `DBL_DECIMAL_DIG l'est). J'ai toujours souhaité un spécificateur de format qui arrondirait toujours exactement à la précision maximale (au lieu des 6 décimales codées en dur).La réponse courte pour imprimer les nombres à virgule flottante sans perte (de sorte qu'ils puissent être lus exactement au même nombre, sauf NaN et Infinity):
printf("%.9g", number)
.printf("%.17g", number)
.NE PAS utiliser
%f
, car cela spécifie uniquement le nombre de chiffres significatifs après la décimale et tronquera les petits nombres. Pour référence, les nombres magiques 9 et 17 peuvent être trouvés dansfloat.h
lequel définitFLT_DECIMAL_DIG
etDBL_DECIMAL_DIG
.la source
%g
prescripteur?double
valeurs juste au- dessus0.1
:1.000_0000_0000_0000_2e-01
,1.000_0000_0000_0000_3e-01
besoin de 17 chiffres pour distinguer."%.16g"
est insuffisant et"%.17g"
et"%.16e"
sont suffisantes. Les détails de%g
, ont été mal rappelés par moi.Si vous n'êtes intéressé que par le bit (motif hexadécimal resp), vous pouvez utiliser le
%a
format. Cela vous garantit:Je dois ajouter que ce n'est disponible que depuis C99.
la source
Non, il n'existe pas de spécificateur de largeur printf pour imprimer en virgule flottante avec une précision maximale . Laissez-moi vous expliquer pourquoi.
La précision maximale de
float
etdouble
est variable et dépend de la valeur réelle defloat
oudouble
.Rappel
float
etdouble
sont stockés au format sign.exponent.mantissa . Cela signifie qu'il y a beaucoup plus de bits utilisés pour la composante fractionnaire pour les petits nombres que pour les grands nombres.Par exemple,
float
peut facilement distinguer entre 0,0 et 0,1.Mais
float
n'a aucune idée de la différence entre1e27
et1e27 + 0.1
.En effet, toute la précision (qui est limitée par le nombre de bits de mantisse) est utilisée pour la grande partie du nombre, à gauche de la décimale.
Le
%.f
modificateur indique simplement le nombre de valeurs décimales que vous souhaitez imprimer à partir du nombre flottant en ce qui concerne le formatage . Le fait que la précision disponible dépende de la taille du nombre est à vous en tant que programmeur à gérer.printf
ne peut pas / ne gère pas cela pour vous.la source
float
fournis par un , et vous affirmez qu'il n'y a rien de tel (c'est-à-dire qu'il n'y en a pasFLT_DIG
), ce qui est faux.FLT_DIG
ne veut rien dire. Cette réponse affirme que le nombre de décimales disponibles dépend de la valeur à l'intérieur du flottant .Utilisez simplement les macros de
<float.h>
et le spécificateur de conversion à largeur variable (".*"
):la source
printf("%." FLT_DIG "f\n", f);
%e
, pas si bien pour%f
: seulement s'il sait que la valeur à imprimer est proche1.0
.%e
imprime les chiffres significatifs pour les très petits nombres et%f
ne le fait pas. par exemplex = 1e-100
.%.5f
empreintes0.00000
(une perte totale de précession).%.5e
impressions1.00000e-100
.FLT_DIG
est défini à la valeur à laquelle il est défini pour une raison. Si c'est 6, c'est parce qu'ilfloat
ne peut pas contenir plus de 6 chiffres de précision. Si vous l'imprimez avec%.7f
, le dernier chiffre n'aura aucune signification. Réfléchissez avant de voter contre.%.6f
n'est pas équivalent, parce que ceFLT_DIG
n'est pas toujours 6. Et qui se soucie de l'efficacité? Les E / S sont déjà chères, un chiffre plus ou moins précis ne fera pas de goulot d'étranglement.Je lance une petite expérience pour vérifier que l'impression avec
DBL_DECIMAL_DIG
conserve en effet exactement la représentation binaire du nombre. Il s'est avéré que pour les compilateurs et les bibliothèques C que j'ai essayés,DBL_DECIMAL_DIG
c'est bien le nombre de chiffres requis, et l'impression avec même un chiffre de moins crée un problème important.Je lance cela avec le compilateur C de Microsoft 19.00.24215.1 et la version gcc 7.4.0 20170516 (Debian 6.3.0-18 + deb9u1). L'utilisation d'un chiffre décimal en moins réduit de moitié le nombre de nombres qui se comparent exactement égaux. (J'ai également vérifié que
rand()
tel qu'utilisé produit en effet environ un million de nombres différents.) Voici les résultats détaillés.Microsoft C
GCC
la source
RAND_MAX == 32767
. Considérezu.s[j] = (rand() << 8) ^ rand();
ou similaire pour vous assurer que tous les bits ont une chance d'être 0 ou 1.Dans l'un de mes commentaires à une réponse, j'ai déploré le fait que je souhaitais depuis longtemps un moyen d'imprimer tous les chiffres significatifs sous forme de virgule flottante sous forme décimale, à peu près de la même manière que la question le demande. Eh bien, je me suis finalement assis et je l'ai écrit. Ce n'est pas tout à fait parfait, et c'est un code de démonstration qui imprime des informations supplémentaires, mais cela fonctionne principalement pour mes tests. S'il vous plaît laissez-moi savoir si vous (c'est-à-dire quelqu'un) souhaitez une copie de l'ensemble du programme wrapper qui le pilote pour le test.
la source
À ma connaissance, il existe un algorithme bien diffusé permettant de sortir le nombre nécessaire de chiffres significatifs de telle sorte que lors du balayage de la chaîne, la valeur en virgule flottante d'origine est acquise en
dtoa.c
écriture par Daniel Gay, qui est disponible ici sur Netlib (voir également le papier associé ). Ce code est utilisé par exemple en Python, MySQL, Scilab et bien d'autres.la source