164 votes

Spécification de la largeur de l'impression pour maintenir la précision de la valeur en virgule flottante.

Y a-t-il un printf qui peut être appliqué à un spécificateur à virgule flottante qui formaterait automatiquement la sortie au nombre nécessaire de caractères. chiffres significatifs de telle sorte que lors de la réinscription de la chaîne de caractères, la valeur en virgule flottante d'origine est acquise ?

Par exemple, supposons que j'imprime un float à une précision de 2 décimales :

float foobar = 0.9375;
printf("%.2f", foobar);    // prints out 0.94

Quand je scanne la sortie 0.94 je n'ai aucune garantie conforme aux normes que j'obtiendrai l'original. 0.9375 valeur à virgule flottante (dans cet exemple, je ne le ferai probablement pas).

J'aimerais pouvoir dire printf pour imprimer automatiquement la valeur à virgule flottante au nombre nécessaire de chiffres significatifs afin de s'assurer qu'il peut être scanné pour retrouver la valeur originale transmise à printf .

Je pourrais utiliser certaines des macros dans float.h a dériver la largeur maximale à passer à printf mais existe-t-il déjà un spécificateur permettant d'imprimer automatiquement le nombre nécessaire d'exemplaires d'une carte de crédit ? chiffres significatifs -- ou au moins à la largeur maximale ?

120voto

chux Points 13185

Je recommande la solution hexadécimale de @Jens Gustedt : utiliser %a.

Le PO 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 :

#include <float.h>
int Digs = DECIMAL_DIG;
double OneSeventh = 1.0/7.0;
printf("%.*e\n", Digs, OneSeventh);
// 1.428571428571428492127e-01

Mais creusons un peu plus...

Mathématiquement, la réponse est "0,142857 142857 142857 ...", mais nous utilisons des nombres à virgule flottante de précision finie. Supposons que Binaire double précision IEEE 754 . Ainsi, le OneSeventh = 1.0/7.0 donne la valeur ci-dessous. Sont également indiquées les valeurs représentables précédentes et suivantes double des nombres à virgule flottante.

OneSeventh before = 0.1428571428571428 214571170656199683435261249542236328125
OneSeventh        = 0.1428571428571428 49212692681248881854116916656494140625
OneSeventh after  = 0.1428571428571428 769682682968777953647077083587646484375

Impression de la exact représentation décimale d'un double a des usages limités.

Le C possède 2 familles de macros dans <float.h> pour nous aider.
Le premier ensemble est le nombre de important les chiffres à imprimer dans une chaîne en décimales de sorte que lors de la numérisation de la chaîne en retour, on retrouve la virgule flottante d'origine. Il y a des exemples avec la spécification C minimum et une valeur échantillon Compilateur C11.

FLT_DECIMAL_DIG   6,  9 (float)                           (C11)
DBL_DECIMAL_DIG  10, 17 (double)                          (C11)
LDBL_DECIMAL_DIG 10, 21 (long double)                     (C11)
DECIMAL_DIG      10, 21 (widest supported floating type)  (C99)

Le deuxième ensemble est le nombre de important chiffres une chaîne de caractères peut être scannée en virgule flottante, puis le FP imprimé, tout en conservant la même présentation de la chaîne de caractères. On peut voir avec la spécification C minimum et une valeur échantillon Compilateur C11. Je crois qu'il était disponible avant C99.

FLT_DIG   6, 6 (float)
DBL_DIG  10, 15 (double)
LDBL_DIG 10, 18 (long double)

Le premier ensemble de macros semble répondre à l'objectif de l'OP, à savoir important digits. Mais cela macro n'est pas toujours disponible.

#ifdef DBL_DECIMAL_DIG
  #define OP_DBL_Digs (DBL_DECIMAL_DIG)
#else  
  #ifdef DECIMAL_DIG
    #define OP_DBL_Digs (DECIMAL_DIG)
  #else  
    #define OP_DBL_Digs (DBL_DIG + 3)
  #endif
#endif

Le "+ 3" était le point central de ma réponse précédente. Elle était centrée sur le fait que si l'on connaissait la chaîne de conversion aller-retour-FP-chaîne (ensemble #2 macros disponibles C89), comment déterminer les chiffres pour FP-chaîne-FP (ensemble #1 macros disponibles post C89) ? En général, le résultat était d'ajouter 3.

Maintenant, combien de important chiffres à imprimer est connu et piloté via <float.h> .

Pour imprimer N important chiffres décimaux, on peut utiliser différents formats.

Avec "%e" le précision le champ est le nombre de chiffres après le chiffre de tête et le point décimal. Ainsi, - 1 est en ordre. Remarque : Ce -1 n'est pas dans la version initiale int Digs = DECIMAL_DIG;

printf("%.*e\n", OP_DBL_Digs - 1, OneSeventh);
// 1.4285714285714285e-01

Avec "%f" le précision le champ est le nombre de chiffres après le point décimal. Pour un nombre comme OneSeventh/1000000.0 il faudrait OP_DBL_Digs + 6 pour voir tous les important les chiffres.

printf("%.*f\n", OP_DBL_Digs    , OneSeventh);
// 0.14285714285714285
printf("%.*f\n", OP_DBL_Digs + 6, OneSeventh/1000000.0);
// 0.00000014285714285714285

Note : Beaucoup ont l'habitude de "%f" . Cela affiche 6 chiffres après la virgule ; 6 est l'affichage par défaut, et non la précision du nombre.

116voto

ccxvii Points 566

La réponse courte pour imprimer des nombres à virgule flottante sans perte (de sorte qu'ils puissent être relus en en exactement le même nombre, sauf NaN et Infini) :

  • Si votre type est float : utilisez printf("%.9g", number) .
  • Si votre type est double : utilisez printf("%.17g", number) .

Ne pas utiliser %f car elle ne spécifie que le nombre de chiffres significatifs après la décimale et tronquera les petits nombres. À titre de référence, les nombres magiques 9 et 17 peuvent être trouvés dans float.h qui définit FLT_DECIMAL_DIG y DBL_DECIMAL_DIG .

27voto

Jens Gustedt Points 40410

Si vous n'êtes intéressé que par le bit (motif hexagonal respectif), vous pouvez utiliser la fonction %a format. Cela vous garantit :

Le site par défaut suffit pour une représentation exacte de la valeur si une représentation exacte en base 2 existe et sinon elle est suffisamment grande pour distinguer les valeurs de type double.

Je dois ajouter que ceci n'est disponible que depuis C99.

25voto

bobobobo Points 17477

Non, il n'y en a pas. spécification de la largeur de printf pour imprimer le point flottant avec une précision maximale . Je vais vous expliquer pourquoi.

La précision maximale de float y double es variable et dépendant de la valeur réelle de la float o double .

Rappel float y double sont stockées dans signe.exposant.mantisse format. Cela signifie que il y a beaucoup plus de bits utilisés pour la composante fractionnaire pour les petits nombres. que pour les grands nombres.

enter image description here

Par exemple, float peut facilement distinguer entre 0,0 et 0,1.

float r = 0;
printf( "%.6f\n", r ) ; // 0.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // 0.100000

Mais float n'a aucune idée de la différence entre 1e27 y 1e27 + 0.1 .

r = 1e27;
printf( "%.6f\n", r ) ; // 999999988484154753734934528.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // still 999999988484154753734934528.000000

Cela s'explique par le fait que toute la précision (qui est limitée par le nombre de bits de la mantisse) est utilisée pour la grande partie du nombre, à gauche de la décimale.

El %.f indique simplement le nombre de valeurs décimales que vous souhaitez imprimer à partir du nombre de flottants. mise en forme va. Le fait que le La précision disponible dépend de la taille du nombre est jusqu'à vous en tant que programmeur à gérer. printf ne peut pas/ne veut pas gérer ça pour vous.

12voto

Il suffit d'utiliser les macros de <float.h> et le spécificateur de conversion à largeur variable ( ".*" ) :

float f = 3.14159265358979323846;
printf("%.*f\n", FLT_DIG, f);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X