Évitez les zéros à la fin de printf ()

Je continue à trébucher sur les spécificateurs de format pour la famille de fonctions printf (). Ce que je veux, c’est pouvoir imprimer un double (ou un flottant) avec un nombre maximum donné de chiffres après le point décimal. Si j’utilise:

printf("%1.3f", 359.01335); printf("%1.3f", 359.00999); 

Je reçois

 359.013 359.010 

Au lieu du désiré

 359.013 359.01 

Quelqu’un peut-il m’aider?

Cela ne peut pas être fait avec les printf format printf normaux. Le plus proche que vous pourriez obtenir serait:

 printf("%.6g", 359.013); // 359.013 printf("%.6g", 359.01); // 359.01 

mais le “.6” est la largeur numérique totale afin

 printf("%.6g", 3.01357); // 3.01357 

le casse

Qu’est-ce que vous pouvez faire est de sprintf("%.20g") le nombre à un tampon de chaîne, puis manipuler la chaîne pour avoir uniquement N caractères après le point décimal.

En supposant que votre numéro se trouve dans la variable num, la fonction suivante supprime tous les N sauf les premiers N décimales, puis supprime les zéros à la fin (et le point décimal s’ils étaient tous des zéros).

 char str[50]; sprintf (str,"%.20g",num); // Make the number. morphNumericSsortingng (str, 3); : : void morphNumericSsortingng (char *s, int n) { char *p; int count; p = strchr (s,'.'); // Find decimal point, if any. if (p != NULL) { count = n; // Adjust for more or less decimals. while (count >= 0) { // Maximum decimals allowed. count--; if (*p == '\0') // If there's less than desired. break; p++; // Next character. } *p-- = '\0'; // Truncate ssortingng. while (*p == '0') // Remove trailing zeros. *p-- = '\0'; if (*p == '.') { // If all decimals were zeros, remove ".". *p = '\0'; } } } 

Si vous n’êtes pas satisfait de l’aspect troncature (qui tourne à 0.12399 en 0.12399 au lieu de 0.124 ), vous pouvez utiliser les fonctions d’arrondi déjà fournies par printf . Il vous suffit d’parsingr le numéro à l’avance pour créer dynamicment les largeurs, puis utilisez celles-ci pour transformer le nombre en chaîne:

 #include  void nDecimals (char *s, double d, int n) { int sz; double d2; // Allow for negative. d2 = (d >= 0) ? d : -d; sz = (d >= 0) ? 0 : 1; // Add one for each whole digit (0.xx special case). if (d2 < 1) sz++; while (d2 >= 1) { d2 /= 10.0; sz++; } // Adjust for decimal point and fractionals. sz += 1 + n; // Create format ssortingng then use it. sprintf (s, "%*.*f", sz, n, d); } int main (void) { char str[50]; double num[] = { 40, 359.01335, -359.00999, 359.01, 3.01357, 0.111111111, 1.1223344 }; for (int i = 0; i < sizeof(num)/sizeof(*num); i++) { nDecimals (str, num[i], 3); printf ("%30.20f -> %s\n", num[i], str); } return 0; } 

Tout l’intérêt de nDecimals() dans ce cas est de déterminer correctement les largeurs de champ, puis de formater le nombre en utilisant une chaîne de format basée sur cela. Le harnais de test main() montre ceci en action:

  40.00000000000000000000 -> 40.000 359.01335000000000263753 -> 359.013 -359.00999000000001615263 -> -359.010 359.00999999999999090505 -> 359.010 3.01357000000000008200 -> 3.014 0.11111111099999999852 -> 0.111 1.12233439999999995429 -> 1.122 

Une fois que vous avez la valeur correctement arrondie, vous pouvez encore la transmettre à morphNumericSsortingng() pour supprimer les zéros de fin en modifiant simplement:

 nDecimals (str, num[i], 3); 

dans:

 nDecimals (str, num[i], 3); morphNumericSsortingng (str, 3); 

(ou appeler morphNumericSsortingng à la fin de nDecimals mais, dans ce cas, je combinerais probablement les deux en une seule fonction), et vous vous retrouvez avec:

  40.00000000000000000000 -> 40 359.01335000000000263753 -> 359.013 -359.00999000000001615263 -> -359.01 359.00999999999999090505 -> 359.01 3.01357000000000008200 -> 3.014 0.11111111099999999852 -> 0.111 1.12233439999999995429 -> 1.122 

Pour vous débarrasser des zéros, vous devez utiliser le format “% g”:

 float num = 1.33; printf("%g", num); //output: 1.33 

Une fois que la question a été clarifiée un peu, la suppression des zéros n’est pas la seule chose demandée, mais il fallait également limiter la sortie à trois décimales. Je pense que cela ne peut pas être fait avec les chaînes de format sprintf seulement. Comme Pax Diablo l’a souligné, la manipulation de chaînes serait nécessaire.

J’aime la réponse de R. légèrement modifiée:

 float f = 1234.56789; printf("%d.%.0f", f, 1000*(f-(int)f)); 

‘1000’ détermine la précision.

Puissance au 0,5 arrondi.

MODIFIER

Ok, cette réponse a été modifiée plusieurs fois et j’ai perdu la trace de ce que je pensais il y a quelques années (et à l’origine, elle ne remplissait pas tous les critères). Voici donc une nouvelle version (qui remplit tous les critères et gère correctement les nombres négatifs):

 double f = 1234.05678900; char s[100]; int decimals = 10; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf("10 decimals: %d%s\n", (int)f, s+1); 

Et les cas de test:

 #import  #import  #import  int main(void){ double f = 1234.05678900; char s[100]; int decimals; decimals = 10; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf("10 decimals: %d%s\n", (int)f, s+1); decimals = 3; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" 3 decimals: %d%s\n", (int)f, s+1); f = -f; decimals = 10; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" negative 10: %d%s\n", (int)f, s+1); decimals = 3; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" negative 3: %d%s\n", (int)f, s+1); decimals = 2; f = 1.012; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" additional : %d%s\n", (int)f, s+1); return 0; } 

Et la sortie des tests:

  10 decimals: 1234.056789 3 decimals: 1234.057 negative 10: -1234.056789 negative 3: -1234.057 additional : 1.01 

Maintenant, tous les critères sont remplis:

  • le nombre maximum de décimales derrière le zéro est fixé
  • les zéros sont supprimés
  • ça le fait mathématiquement non (non?)
  • fonctionne (maintenant) aussi quand la première décimale est zéro

Malheureusement, cette réponse est une double ligne car sprintf ne renvoie pas la chaîne.

Une solution simple mais qui fait le travail, assigne une longueur et une précision connues et évite les chances de format exponentiel (ce qui représente un risque lorsque vous utilisez% g):

 // Since we are only interestd in 3 decimal places, this function // can avoid any potential miniscule floating point differences // which can return false when using "==" int DoubleEquals(double i, double j) { return (fabs(i - j) < 0.000001); } void PrintMaxThreeDecimal(double d) { if (DoubleEquals(d, floor(d))) printf("%.0f", d); else if (DoubleEquals(d * 10, floor(d * 10))) printf("%.1f", d); else if (DoubleEquals(d * 100, floor(d* 100))) printf("%.2f", d); else printf("%.3f", d); } 

Ajouter ou supprimer "elses" si vous voulez un maximum de 2 décimales; 4 décimales; etc.

Par exemple si vous vouliez 2 décimales:

 void PrintMaxTwoDecimal(double d) { if (DoubleEquals(d, floor(d))) printf("%.0f", d); else if (DoubleEquals(d * 10, floor(d * 10))) printf("%.1f", d); else printf("%.2f", d); } 

Si vous souhaitez spécifier la largeur minimale pour conserver les champs alignés, incrémentez-les si nécessaire, par exemple:

 void PrintAlignedMaxThreeDecimal(double d) { if (DoubleEquals(d, floor(d))) printf("%7.0f", d); else if (DoubleEquals(d * 10, floor(d * 10))) printf("%9.1f", d); else if (DoubleEquals(d * 100, floor(d* 100))) printf("%10.2f", d); else printf("%11.3f", d); } 

Vous pouvez également convertir cela en une fonction où vous passez la largeur souhaitée du champ:

 void PrintAlignedWidthMaxThreeDecimal(int w, double d) { if (DoubleEquals(d, floor(d))) printf("%*.0f", w-4, d); else if (DoubleEquals(d * 10, floor(d * 10))) printf("%*.1f", w-2, d); else if (DoubleEquals(d * 100, floor(d* 100))) printf("%*.2f", w-1, d); else printf("%*.3f", w, d); } 

Je recherche la chaîne (en partant de la droite) pour le premier caractère dans la plage 1 à 9 (valeur ASCII 4957 ) puis null (la valeur 0 ) à chaque caractère – voir ci-dessous:

 void ssortingpTrailingZeros(void) { //This finds the index of the rightmost ASCII char[1-9] in array //All elements to the left of this are nulled (=0) int i = 20; unsigned char char1 = 0; //initialised to ensure entry to condition below while ((char1 > 57) || (char1 < 49)) { i--; char1 = sprintfBuffer[i]; } //null chars left of i for (int j = i; j < 20; j++) { sprintfBuffer[i] = 0; } } 

Qu’en est-il de quelque chose comme ceci (peut avoir des erreurs d’arrondi et des problèmes de valeur négative qui nécessitent un débogage, laissé comme exercice pour le lecteur):

 printf("%.0d%.4g\n", (int)f/10, f-((int)f-(int)f%10)); 

C’est légèrement programmatique mais au moins cela ne vous fait pas manipuler de chaîne.

Voici mon premier essai de réponse:

 vide
 xprintfloat (format char *, float f)
 {
   chars [50];
   char * p;

   sprintf (s, format, f);
   pour (p = s; * p; ++ p)
     si ('.' == * p) {
       while (* ++ p);
       while ('0' == * - p) * p = '\ 0';
     }
   printf ("% s", s);
 }

Bogues connus: Dépassement possible du tampon en fonction du format. Si “.” est présent pour une autre raison que% de faux résultat pourrait arriver.

Légère variation sur ci-dessus: –

  1. Élimine la période pour le cas (10000.0).
  2. Pauses après le traitement de la première période.

Code ici: –

 void EliminateTrailingFloatZeros(char *iValue) { char *p = 0; for(p=iValue; *p; ++p) { if('.' == *p) { while(*++p); while('0'==*--p) *p = '\0'; if(*p == '.') *p = '\0'; break; } } } 

Il y a toujours un potentiel de débordement, alors soyez prudent. P

Pourquoi ne pas simplement faire ça?

 double f = 359.01335; printf("%g", round(f * 1000.0) / 1000.0); 

J’ai trouvé des problèmes dans certaines des solutions affichées. Je mets ça ensemble en me basant sur les réponses ci-dessus. Il semble fonctionner pour moi.

 int doubleEquals(double i, double j) { return (fabs(i - j) < 0.000001); } void printTruncatedDouble(double dd, int max_len) { char str[50]; int match = 0; for ( int ii = 0; ii < max_len; ii++ ) { if (doubleEquals(dd * pow(10,ii), floor(dd * pow(10,ii)))) { sprintf (str,"%f", round(dd*pow(10,ii))/pow(10,ii)); match = 1; break; } } if ( match != 1 ) { sprintf (str,"%f", round(dd*pow(10,max_len))/pow(10,max_len)); } char *pp; int count; pp = strchr (str,'.'); if (pp != NULL) { count = max_len; while (count >= 0) { count--; if (*pp == '\0') break; pp++; } *pp-- = '\0'; while (*pp == '0') *pp-- = '\0'; if (*pp == '.') { *pp = '\0'; } } printf ("%s\n", str); } int main(int argc, char **argv) { printTruncatedDouble( -1.999, 2 ); // prints -2 printTruncatedDouble( -1.006, 2 ); // prints -1.01 printTruncatedDouble( -1.005, 2 ); // prints -1 printf("\n"); printTruncatedDouble( 1.005, 2 ); // prints 1 (should be 1.01?) printTruncatedDouble( 1.006, 2 ); // prints 1.01 printTruncatedDouble( 1.999, 2 ); // prints 2 printf("\n"); printTruncatedDouble( -1.999, 3 ); // prints -1.999 printTruncatedDouble( -1.001, 3 ); // prints -1.001 printTruncatedDouble( -1.0005, 3 ); // prints -1.001 (shound be -1?) printTruncatedDouble( -1.0004, 3 ); // prints -1 printf("\n"); printTruncatedDouble( 1.0004, 3 ); // prints 1 printTruncatedDouble( 1.0005, 3 ); // prints 1.001 printTruncatedDouble( 1.001, 3 ); // prints 1.001 printTruncatedDouble( 1.999, 3 ); // prints 1.999 printf("\n"); exit(0); } 

Certaines des solutions hautement votées suggèrent le spécificateur de conversion %g de printf . C’est faux car il y a des cas où %g produira une notation scientifique. D’autres solutions utilisent les mathématiques pour imprimer le nombre souhaité de chiffres décimaux.

Je pense que la solution la plus simple consiste à utiliser sprintf avec le spécificateur de conversion %f et à supprimer manuellement les zéros de fin et éventuellement un point décimal du résultat. Voici une solution C99:

 #include  #include  char* format_double(double d) { int size = snprintf(NULL, 0, "%.3f", d); char *str = malloc(size + 1); snprintf(str, size + 1, "%.3f", d); for (int i = size - 1, end = size; i >= 0; i--) { if (str[i] == '0') { if (end == i + 1) { end = i; } } else if (str[i] == '.') { if (end == i + 1) { end = i; } str[end] = '\0'; break; } } return str; } 

Notez que les caractères utilisés pour les chiffres et le séparateur décimal dépendent des parameters régionaux en vigueur. Le code ci-dessus suppose un environnement linguistique anglais ou américain.

Votre code arrondit à trois décimales en raison du “.3” avant le f

 printf("%1.3f", 359.01335); printf("%1.3f", 359.00999); 

Ainsi, si vous arrondissez la deuxième ligne à deux décimales, vous devriez la changer comme suit:

 printf("%1.3f", 359.01335); printf("%1.2f", 359.00999); 

Ce code affichera les résultats souhaités:

 359.013 359.01 

* Notez que cela suppose que vous l’imprimez déjà sur des lignes séparées, sinon les éléments suivants l’empêcheront d’imprimer sur la même ligne:

 printf("%1.3f\n", 359.01335); printf("%1.2f\n", 359.00999); 

Le code source du programme suivant a été mon test pour cette réponse

 #include  int main() { printf("%1.3f\n", 359.01335); printf("%1.2f\n", 359.00999); while (true){} return 0; }