Printf spécificateur de largeur pour maintenir la précision de la valeur à virgule flottante
Est-il un printf
spécificateur de largeur qui peut être appliquée à un virgule flottante spécificateur qui serait automatiquement le format de sortie pour le nombre nécessaire de chiffres significatifs tels que lors de la numérisation de la chaîne de retour dans, l'original de la valeur à virgule flottante est acquis?
Par exemple, supposons que j'ai l'impression d'un float
à une précision de 2
décimales:
float foobar = 0.9375;
printf("%.2f", foobar); //prints out 0.94
Quand je scan la sortie 0.94
, je n'ai pas conformes aux normes garantie que je vais obtenir l'original 0.9375
à virgule flottante valeur de retour (dans cet exemple, je ne sera probablement pas).
Je voudrais une façon de dire printf
pour imprimer automatiquement la valeur à virgule flottante pour le nombre nécessaire de chiffres significatifs pour s'assurer qu'il peut être analysé en arrière à l'original de la valeur passée à printf
.
Je pourrais utiliser des macros dans float.h
à tirer le maximum de largeur de passer à printf
, mais il y a déjà un rédacteur de devis pour imprimer automatiquement le nombre nécessaire de chiffres significatifs -- ou au moins à la largeur maximale?
- Il me semble que 6 est déjà le plus de précision possible la
float
type d'offre. (Valeurs décimales après la 6ème sera probablement de plus en plus loin off). Regardez en haut de la réelle#define
d valeur deFLT_DIG
sur votre plate-forme, tous les paris dire, il va juste être 6. - Si vous êtes juste de recommander que l'on utilise une hypothèse de l'air au lieu de prendre le portable d'approche?
- Non, je ne recommanderais pas à l'aide de "l'hypothèse de l'air", je conseille
printf( "%f", val );
qui est déjà un portable, efficace, et la valeur par défaut. - De sorte que je puisse l'ajouter à la les réponses, seriez-vous capable de citer la clause standard C99, qui stipule que l'instruction printf affiche le type float à un maximum de précision par défaut si aucune précision n'est indiquée?
- Bien que @chux pénètre, il y a quelques assez compliqué de mathématiques à l'effectif de la précision pour votre
double
. Comme votredouble
devient extrêmement large (très loin de 1.0), elle est moins précise dans la partie décimale (la valeur de la partie inférieure à 1.0). Donc vous ne pouvez pas vraiment obtenir une réponse satisfaisante ici, parce que votre question a une hypothèse fausse en elle (à savoir que tous lesfloat
s/double
s sont créés égaux) - Je m'excuse, je pense que vous avez peut-être mal compris la question. Je suis intéressé par la précision de la sortie (c'est à dire le nombre de caractères imprimés), pas la précision des types de données (c'est à dire la précision float/double représentent la valeur vraie).
%.12f
va, il suffit d'imprimer 12 décimales, peu importe si cette précision est disponible à partir de votre type de variable ou pas. Vous devez considérer que le "imprimé" précision disponible (# caractères imprimés) est complètement arbitraire. La précision disponible dans unfloat
oudouble
dépend du nombre de bits de la mantisse utilisé dans le type de données, ainsi que sur la taille de la nombre.printf
ne pas tenir compte de la taille du nombre cependant. Si la réponse à votre question est n, vous ne pouvez pas imprimer un nombre avecprintf
"au maximum de précision possible"- Gris Voir vous ai fait une petite mise à jour en insistant sur
float
. Pourfloat
, je vous conseille d'utiliserprintf("%.*e", OP_FLT_Digs-1, x)
où OP_FLT_Digs est dérivé conséquence que OP_DBL_Digs ci-dessous. À mon humble avis, votre point estfloat-text-float
aller-retour et c'est exactement, par C spec, ce xxx_DECIMAL_DIG fournir. Bien sûr%a
est grande, mais je suppose que vous préférez décimal texte. - Gris C11dr 5.2.4.2.2 "... nombre de chiffres après la virgule, n, telle que tout nombre à virgule flottante avec p radix b chiffres peuvent être arrondie à un nombre à virgule flottante avec n chiffres après la virgule et le dos encore une fois sans changement de la valeur, p log10 b b est une puissance de 10 ⎡1 + p log10 b⎤ sinon FLT_DECIMAL_DIG 6 DBL_DECIMAL_DIG 10 LDBL_DECIMAL_DIG 10 ..." La 6,10,10 sont les valeurs minimales.
- Je suis surpris que personne n'a (semble-t-il) a fait référence à l'article "Ce que Chaque Programmeur Doit Savoir à Propos de l'Arithmétique à virgule Flottante" - une réédition de ce qui peut être trouvé à l'adresse docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html#1251. Il y a aussi, il semble un site web qui tente de simplifier ce qui peut être trouvé à l'adresse floating-point-gui.de (non ce n'est pas en allemand mais en anglais).
Vous devez vous connecter pour publier un commentaire.
Je recommande @Jens Gustedt hexadécimal solution: utiliser le %de un.
OP veut “impression avec le maximum de précision (ou au moins les plus importantes décimal)”.
Un exemple simple serait d'imprimer une septième comme dans:
Mais nous allons aller plus loin ...
Mathématiquement, la réponse est "0.142857 142857 142857 ...", mais nous sommes finies de précision des nombres à virgule flottante.
Supposons La norme IEEE 754 double précision binaire.
Ainsi, le
OneSeventh = 1.0/7.0
résultats dans la valeur ci-dessous. Est également indiqué que le précédant et suivant représentabledouble
des nombres à virgule flottante.L'impression de la exacte représentation décimale d'un
double
a des usages limités.C a 2 familles de macros dans
<float.h>
pour nous aider.Le premier set est le nombre de significative chiffres à imprimer dans une chaîne de caractères en nombre décimal, donc lors de la numérisation de la chaîne de retour,
nous obtenons l'original en virgule flottante. Y sont présentés avec le C spec minimum valeur et un échantillon C11 compilateur.
La deuxième série est le nombre de significative chiffres d'une chaîne peuvent être numérisés en un flottant et puis la FP imprimé, en conservant la même chaîne de présentation. Y sont présentés avec le C spec minimum valeur et un échantillon C11 compilateur. Je crois disponible en pré-C99.
La première série de macros semble répondre à l'OP de l'objectif de significative chiffres. Mais que macro n'est pas toujours disponible.
Le "+ 3" est le fond de ma réponse précédente.
Son centrée sur si le fait de connaître l'aller-retour de conversion string-FP-chaîne (set #2 macros disponibles C89), comment pourrait-on déterminer les chiffres pour FP-chaîne-FP (set n ° 1 des macros disponibles après les C89)? En général, ajouter 3 a été le résultat.
Maintenant, combien de significative chiffres à imprimer est connu et par l'intermédiaire d'
<float.h>
.Pour imprimer N significative chiffres décimaux on peut utiliser différents formats.
Avec
"%e"
, le précision champ est le nombre de chiffres après le chef de chiffres et point décimal.Donc
- 1
est dans l'ordre. Remarque: Cette-1 is not in the initial
int Creuse = DECIMAL_DIG;`Avec
"%f"
, le précision champ est le nombre de chiffres après le point décimal.Pour un certain nombre, comme
OneSeventh/1000000.0
, on aurait besoinOP_DBL_Digs + 6
pour voir tous les significative chiffres.Remarque: la plupart sont utiliser pour
"%f"
. Qui affiche 6 chiffres après la virgule; 6, c'est l'affichage par défaut, pas la précision d'un nombre.%f
est 6.FLT_DECIMAL_DIG 6, 9
). C'est que de 32 bits arc vs 64 bits?printf("%.*f")
à imprimer après la virgule nécessite soustrayant le nombre de chiffres qui sera imprimé avant la virgule de la valeur donnée parDBL_DIG
. E. g.:printf("%.*f", DBL_DIG - (int) lrint(ceil(log10(ceil(fabs(dval))))), dval);
DBL_DECIMAL_DIG
chiffres significatifs sont imprimés, des chiffres supplémentaires ne servent à rien "pour maintenir la précision" et de récupérer l'originaldouble
. D'où cette réponse est"%.*e\n", DBL_DECIMAL_DIG - 1, x
approche."%f"
en premier lieu. À l'aide de"%e"
que vous avez montré est bien sûr une meilleure approche tout rond et efficacement un décent réponse (même si c'est peut-être pas aussi bon que l'utilisation de"%a"
peut-être si elle est disponible, et bien sûr"%a"
devrait être disponible si " DBL_DECIMAL_DIG est). J'ai toujours souhaité pour un spécificateur de format qui serait toujours ronde exactement le maximum de précision (au lieu de le codé en dur 6 décimales).La réponse courte à imprimer des nombres à virgule flottante sans perte (tels qu'ils peuvent être lus
de retour à exactement le même nombre, à l'exception de NaN et de l'Infini):
printf("%.9g", number)
.printf("%.17g", number)
.Ne PAS utiliser
%f
, depuis que seuls spécifie le nombre de chiffres significatifs après la virgule et tronques de petits nombres. Pour référence, la magie des nombres 9 et 17 peuvent être trouvés dansfloat.h
qui définitFLT_DECIMAL_DIG
etDBL_DECIMAL_DIG
.%g
spécificateur?double
valeurs, juste au-dessus de0.1
:1.000_0000_0000_0000_2e-01
,1.000_0000_0000_0000_3e-01
besoin de 17 chiffres à distinguer.DBL_DECIMAL_DIG
, à l'instar de 17 ans, est le total nombre de chiffres significatifs nécessaires.%.16g
et%.16e
d'impression 1 chiffre avant la virgule et 16 décimales par la suite. Doncprintf("%.*g", DBL_DECIMAL_DIG - 1, number)
est suffisant. Bien sûr, un chiffre supplémentaire comme dans"%.17g"
est OK."%.16g"
est insuffisante et"%.17g"
et"%.16e"
sont suffisantes. Les détails de%g
, ont été mal souvenu de moi.Si vous êtes uniquement intéressé par le bit (resp modèle hexagonal), vous pouvez utiliser le
%a
format. Cela vous garantit:Je dois ajouter que ce n'est disponible que depuis le C99.
Non, il n'y a pas une telle printf spécificateur de largeur d'impression à virgule flottante avec un maximum de précision. Laissez-moi vous expliquer pourquoi.
Le maximum de précision de
float
etdouble
est variable, et dépend de la valeur réelle de lafloat
oudouble
.Rappel
float
etdouble
sont stockés dans signe.l'exposant.mantisse format. Cela signifie que il y a beaucoup plus de bits utilisés pour la fraction de la composante 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
.C'est parce que toute la précision (qui est limité par le nombre de bits de la mantisse) est utilisé pour la grande partie du nombre, à gauche de la décimale.
La
%.f
modificateur dit juste combien de valeurs décimales que vous souhaitez imprimer à partir du nombre à virgule autant que mise en forme va. Le fait que le la précision dépend de la taille du nombre est à vous que le programmeur à manipuler.printf
ne peut pas/ne gère pas que pour vous.float
fournit, et vous affirmer qu'il n'y a pas une telle chose (j'. e. qu'il n'y a pas deFLT_DIG
), ce qui est faux.FLT_DIG
ne veut rien dire. Cette réponse affirme le nombre de décimales disponible dépend de la valeur à l'intérieur de l'float.Simplement utiliser les macros de
<float.h>
et la largeur variable indicateur de conversion (".*"
):printf("%." FLT_DIG "f\n", f);
%e
, pas pour%f
: seulement si c'est sait que la valeur d'impression est proche de1.0
.printf()
- qui lui-même est assez cher.printf
lui-même interpréter le format de chaîne de caractères passée?%f
?%e
?%e
tirages de chiffres significatifs pour les très petits nombres et%f
ne le fait pas. par exemple,x = 1e-100
.%.5f
imprime0.00000
(une perte totale de précession).%.5e
imprime1.00000e-100
.0.0000005
comme undouble
, il en existe, mais pas au point qu'il devrait perdre de tous précision et être affiché en tant que0
.FLT_DIG
est en fait#define
d 6 sur Mac OS X. Même en utilisant%.7f
les rendements des résultats plus précis que cela.FLT_DIG
est défini à la valeur qu'elle est définie à pour une raison. Si c'est 6, c'est parce quefloat
n'est pas capable de tenir plus de 6 chiffres de précision. Si vous imprimez à l'aide de%.7f
, le dernier chiffre n'ont pas de signification. Pensez avant de vous downvote.%.6f
est entièrement équivalent et moins cher.%.6f
n'est pas équivalent, parce queFLT_DIG
n'est pas toujours 6. Et qui se soucie de l'efficacité? I/O est déjà cher comme l'enfer, un chiffre plus ou moins de précision ne fera pas un goulot d'étranglement.Dans un de mes commentaires pour la réponse, je déplore que j'ai longtemps voulu d'une certaine façon à l'impression de tous les chiffres significatifs dans une valeur à virgule flottante en forme décimale, de la même façon la question se pose. Eh bien j'ai enfin s'assit et écrivit-il. Ce n'est pas tout à fait parfaite, et c'est le code de démonstration qui imprime des informations supplémentaires, mais il a surtout pour mes tests. S'il vous plaît laissez-moi savoir si vous (c'est à dire n'importe qui) serait comme une copie de l'ensemble du programme d'enveloppe qui l'anime pour le test.
Je dirige une petite expérience afin de vérifier que l'impression avec
DBL_DECIMAL_DIG
, en effet, exactement préserver le numéro de la représentation binaire. Il s'est avéré que pour les compilateurs et bibliothèques C, j'ai essayé,DBL_DECIMAL_DIG
est en effet le nombre de chiffres requis, et l'impression, même avec un chiffre moins crée un problème important.Je l'exécute avec Microsoft compilateur C 19.00.24215.1 et gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1). En utilisant un de moins chiffres de moitié le nombre de numéros de comparer exactement égale. (J'ai aussi vérifié que
rand()
utilisé, en effet, produit environ un million de numéros différents.) Voici les résultats détaillés.Microsoft C
GCC
À ma connaissance, il est bien diffusée algorithme permettant de de sortie pour le nombre de chiffres significatifs tels que lors de la numérisation de la chaîne de retour dans, l'original de la valeur à virgule flottante est acquis dans
dtoa.c
écrit par Daniel Gay, qui est disponible ici sur Netlib (voir aussi les associés papier). Ce code est utilisé par exemple en Python, MySQL, Scilab, et beaucoup d'autres.