tags:

views:

4630

answers:

5

I keep stumbling on the format specifiers for the printf() family of functions. What I want is to be able to print a double (or float) with a maximum given number of digits after the decimal point. If I use:

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

I get

359.013
359.010

Instead of the desired

359.013
359.01

Can anybody help me?

+3  A: 

To get rid of the trailing zeros, you should use the "%g" format:

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

After the question was clarified a bit, that suppressing zeros is not the only thing that was asked, but limiting the output to three decimal places was required as well. I think that can't be done with sprintf format strings alone. As Pax Diablo pointed out, string manipulation would be required.

Tomalak
See the MSDN help page: http://msdn.microsoft.com/en-us/library/0ecbz014(VS.80).aspx
xtofl
@Tomalak: This does not do what I want. I want to be able to specify a maximum number of digits after the decimal point. This is: I want 1.3347 to be printed "1.335" and 1.3397 to be printed "1.34"@xtofl: I had already checked that, but I still can't see the answer to my problem
Gorpik
I have edited my question in order to improve the example, so what I need is clearer.
Gorpik
+9  A: 

This can't be done with the normal printf format specifiers. The closet you could get would be:

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

but the ".6" is the total numeric width so

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

breaks it.

What you probably need to do is to sprintf("%.20g") the number to a string buffer then manipulate the string to only have N characters past the decimal point.

Assuming your number is in the variable num, the following function will remove all but the first N decimals, then strip off the trailing zeros (and decimal point if they were all zeros).

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 (str,'.');       // Find decimal point, if any.
    if (p != NULL) {
        count = 3;              // 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';
        }
    }
}
paxdiablo
Yes, I feared so. Thanks.
Gorpik
A: 

Here is my first try at an answer:

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

Known bugs: Possible buffer overflow depending on format. If "." is present for other reason than %f wrong result might happen.

Your known bugs are the same that printf() itself has, so no problems there. But I was looking for a format string that allowed me doing what I wanted, not a programmatic solution, which is what I already have.
Gorpik
A: 

Slight variation on above: -

  1. Eliminates period for case (10000.0).
  2. Breaks after first period is processed.

Code here: -

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

It still has potential for overflow, so be careful ;P

David Thornley
A: 

What about something like this (might have rounding errors and negative-value issues that need debugging, left as an exercise for the reader):

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

It's slightly programmatic but at least it doesn't make you do any string manipulation.

R..