Question

Je n'arrête pas de tomber sur les spécificateurs de format pour la famille de fonctions printf (). Ce que je veux, c'est pouvoir imprimer un double (ou un float) avec un nombre maximum 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 de ce que vous souhaitez

359.013
359.01

Quelqu'un peut-il m'aider?

Était-ce utile?

La solution

Cela ne peut pas être fait avec les spécificateurs de format normaux printf . Le plus proche que vous puissiez obtenir serait:

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

mais le ".6" est la largeur totale numérique ainsi

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

le casse.

Ce que vous pouvez faire, c'est de sprintf ("%. 20g") le nombre dans un tampon de chaîne, puis manipuler la chaîne de manière à ce que N caractères ne soient pas après la virgule. point.

En supposant que votre nombre est dans la variable num, la fonction suivante supprimera toutes les décimales N sauf les premières, puis supprimera les zéros de fin (et le point décimal s'il s'agissait de zéros).

char str[50];
sprintf (str,"%.20g",num);  // Make the number.
morphNumericString (str, 3);
:    :
void morphNumericString (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 string.
        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 transformerait 0.12399 en 0.123 au lieu de l'arrondir à 0.124 ), vous peut réellement utiliser les fonctions d'arrondi déjà fournies par printf . Il vous suffit d'analyser le nombre au préalable pour créer dynamiquement les largeurs, puis de les utiliser pour transformer le nombre en chaîne:

#include <stdio.h>

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 string 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;
}

Le but de nDecimals () est dans ce cas de déterminer correctement la largeur des champs, puis de formater le nombre à l'aide d'une chaîne de format basée sur celle-ci. Le faisceau de test main () le montre 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 correctement arrondi la valeur, vous pouvez à nouveau la transmettre à morphNumericString () pour supprimer les zéros de fin en modifiant simplement:

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

dans:

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

(ou en appelant morphNumericString à 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

Autres conseils

Pour vous débarrasser des zéros à la fin, vous devez utiliser le "% g " format:

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

Après que la question ait été clarifiée un peu, la suppression des zéros n’est pas la seule question qui a été posée, mais il a également été nécessaire de limiter la sortie à trois décimales. Je pense que cela ne peut pas être fait avec les chaînes de format sprintf seules. Comme Pax Diablo , une manipulation de chaîne serait requise.

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 arrondi 0,5.

MODIFIER

Ok, cette réponse a été modifiée plusieurs fois et j’ai perdu la trace de ce que je pensais il ya 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 <stdio.h>
#import <stdlib.h>
#import <math.h>

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

Tous les critères sont remplis:

  • le nombre maximal de décimales derrière le zéro est fixé
  • les zéros de fin sont supprimés
  • ça le fait mathématiquement juste (non?)
  • fonctionne (maintenant) même lorsque la première décimale est zéro

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

Je recherche dans la chaîne (à partir de l'extrême droite) le premier caractère de la plage 1 à 9 (valeur ASCII 49 - 57 ) puis null (défini sur 0 ) chaque caractère à sa droite - voir ci-dessous:

void stripTrailingZeros(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 ça (peut avoir des erreurs d'arrondi et des problèmes de valeur négative nécessitant un débogage, laissé comme exercice pour le lecteur):

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

C’est un peu programmatique, mais au moins, cela ne vous oblige pas à manipuler des chaînes.

Une solution simple mais qui accomplit le travail, attribue une longueur et une précision connues et évite les risques de passer au format exponentiel (ce qui présente un risque lorsque vous utilisez% g):

// Since we are only interested 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 "autres" " 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 garder les champs alignés, incrémentez-le 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 dans laquelle 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);
}

J'ai trouvé des problèmes dans certaines des solutions publiées. J'ai mis cela ensemble sur la base des réponses ci-dessus. Cela 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 solutions très votées suggèrent le spécificateur de conversion % g de printf . Ceci est faux car il existe 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 <stdio.h>
#include <stdlib.h>

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 paramètres régionaux actuels. Le code ci-dessus suppose des paramètres régionaux en anglais C ou américain.

Voici mon premier essai de réponse:

void
xprintfloat(char *format, float f)
{
  char s[50];
  char *p;

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

Bugs connus: Débordement de tampon possible en fonction du format. Si ". & Quot; est présent pour une autre raison que% f, un résultat erroné pourrait se produire.

Pourquoi ne pas simplement faire cela?

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

Légère variation ci-dessus:

  1. Élimine la période pour le cas (10000.0).
  2. Les 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 risque de débordement, soyez donc prudent; P

Votre code est arrondi à trois décimales à cause du signe ".3". avant le f

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

Ainsi, si la deuxième ligne est arrondie à deux décimales, modifiez-la ainsi:

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

Ce code générera les résultats souhaités:

359.013
359.01

* Notez que ceci suppose que vous avez déjà l'impression sur des lignes séparées. Sinon, les éléments suivants l'empêcheront de s'imprimer sur la même ligne:

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

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

#include <cstdio>

int main()
{

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

    while (true){}

    return 0;

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top