开发者

How to hide leading zero in printf

开发者 https://www.devze.com 2022-12-25 19:30 出处:网络
The following outputs 0.23. How do I get it to simply output .23? printf( \"%开发者_Go百科8.2f\" , .23 );

The following outputs 0.23. How do I get it to simply output .23?

printf( "%开发者_Go百科8.2f" , .23 );


The C standard says that for the f and F floating point format specifiers:

If a decimal-point character appears, at least one digit appears before it.

I think that if you don't want a zero to appear before the decimal point, you'll probably have to do something like use snprintf() to format the number into a string, and remove the 0 if the formatted string starts with "0." (and similarly for "-0."). Then pass that formatted string to our real output. Or something like that.


It is not possible to do it only using printf. The documention for printf says:

f  - "double" argument is output in conventional form, i.e.
     [-]mmmm.nnnnnn
     The default number of digits after the decimal point is six,
     but this can be changed with a precision field. If a decimal point
     appears, at least one digit appears before it. The "double" value is
     rounded to the correct number of decimal places.

Note the If a decimal point appears, at least one digit appears before it.

Therefore it seems you have to handcode your own formatter.


double f = 0.23;

assert(f < 0.995 && f >= 0);  
printf(".%02u\n" , (unsigned)((f + 0.005) * 100));


Just convert it to an integer with the required accuracy

double value = .12345678901; // input
int accuracy = 1000; // 3 digit after dot
printf(".%03d\n", (int)(value * accuracy) );

Output:

.123

example source on pastebin


#include <stdio.h>

static void printNoLeadingZeros(double theValue)
{
   char buffer[255] = { '\0' };

   sprintf(buffer, "%.2f", theValue);

   printf("%s\n", buffer + (buffer[0] == '0'));
}

int main()
{
   double values[] = { 0.23, .23, 1.23, 01.23, 001.23, 101.23 };
   int n           = sizeof(values) / sizeof(values[0]);
   int i           = 0;

   while(i < n)
      printNoLeadingZeros(values[i++]);

   return(0);
}


The Standard C library doesn't provide this, so you have to write it yourself. This isn't a rare, one-off requirement. You'll need to write similar functions sooner or later to trim trailing zeros and to add in thousands-separators. So, it pays to not just get the bytes of output you're looking for but to illustrate more generally how to write a strong library. When doing so keep in mind:

  1. figure out how you want to call it. Something like this you write once but call a million times, so make the calling as easy as possible.

  2. then make the test suite exercising all alternatives you can think of

  3. while you're at it, just solve the problem forevermore so you never have to come back to it again (eg, don't hardcode width, precision, go ahead and make versions for leading-plus, e-format, and so on)

  4. make it thread-safe even if you're not using threads (a specific case of point 3, actually)

So working backwards: Thread safety requires allocating storage on the stack, which must be done from the caller. This isn't pretty or fun but just get used to it. It's the C way. Formats can have width, precision, some flags, and a conversion type (f, e, g). So lets make width and precision parameters. Rather than parameterizing the public API fully, I'll just have multiple entry points that say in the function name which flags and conversion type they use.

A pet peeve is that when passing in buffers to functions, the function will need to know the size. But if you make that a separate parameter, it's a pain in the but as 1) the caller has to write it and 2) the caller can get it wrong. So my personal style is to make a masking macro that assumes the buffer is a character array, not a pointer, and that uses sizeof() to pass the size into a more verbose version of the function taking the size.

Here's the mock-up of the simplest way I can think of to call it, with test cases.

(Note COUNT() is a macro I've used weekly for decades to get the number of elements in an array. Standard C, should have had something like this.)

(Note I use a dialect of "Hungarian Notation" here. "d" is a double. "a" is "array of." "sz" is a NUL-terminated string buffer, while "psz" is a pointer to one. The difference between these two is that "sz" can be used with COUNT() or sizeof() to get the array size, while "psz" cannot. "i" is an integer and the specific variable "i" is used for looping.

double ad[] = { 0.0, 1.0, 2.2, 0.3, 0.45, 0.666, 888.99,
                -1.0, -2.2, -0.3, -0.45, -0.666, -888.99 };
char   szBuf[20];

for ( int i = 0; i < COUNT( ad ); i++ )
    printf( "%s\n", NoLeadingZeroF( 4, 2, ad[i], szBuf ) );

for ( int i = 0; i < COUNT( ad ); i++ )
    printf( "%s\n", NoLeadingZeroPlusF( 4, 2, ad[i], szBuf ) );

Now, the "f" and "+f" versions seem very similar, so lets have them both call an internal function. Here are the functions, which take the buffer size, and macros that figure it out themselves. (Parallel functions are also written for e and g formats.)

char* NoLeadingZeroFN( int iWidth, int iPrecision, double d, char* szBuf, int iBufLen ) {
  return NoLeadingZeroFmtN( "%*.*f", iWidth, iPrecision, d, szBuf, iBufLen );
}

char* NoLeadingZeroPlusFN( int iWidth, int iPrecision, double d, char* szBuf, int iBufLen ) {
  return NoLeadingZeroFmtN( "%+*.*f", iWidth, iPrecision, d, szBuf, iBufLen );
}


#define NoLeadingZeroF( width, precision, number, buf ) \
        NoLeadingZeroFN( ( width ), (precision ), ( number ), ( buf ), sizeof( buf ) ) 

#define NoLeadingZeroPlusF( width, precision, number, buf ) \
        NoLeadingZeroPlusFN( ( width ), (precision ), ( number ), ( buf ), sizeof( buf ) ) 

Finally the (internal) function that does the work. Note that snprintf() needs a prepended underscore on Windows, but not on Unix.

char* NoLeadingZeroFmtN( char* szFmt, int iWidth, int iPrecision, double d, char* szBuf, int iBufLen ) {

#ifdef WIN32
  _snprintf( szBuf, iBufLen - 1, szFmt, iWidth, iPrecision, d );
#else
  snprintf( szBuf, iBufLen - 1, szFmt, iWidth, iPrecision, d );
#endif

  // Some snprintf()'s do not promise to NUL-terminate the string, so do it ourselves.
  szBuf[ iBufLen - 1 ] = '\0';

  // _snprintf() returns the length actually produced, IF the buffer is big enough.
  // But we don't know it was, so measure what we actually got.
  char* pcTerminator = strchr( szBuf, '\0' );

  for ( char* pcBuf = szBuf; *pcBuf && *pcBuf != '.'; pcBuf++ )
      if ( *pcBuf == '0' ) {
          memmove( pcBuf, pcBuf + 1, pcTerminator - pcBuf );
          break;
      }

  return szBuf;
}

The output is:

.00
1.00
2.20
.30
.45
.67
888.99
-1.00
-2.20
-.30
-.45
-.67
-888.99
+.00
+1.00
+2.20
+.30
+.45
+.67
+888.99
-1.00
-2.20
-.30
-.45
-.67
-888.99

Additional testing should verify that the functions work with buffers that are too small.


It looks there is no easy solution. I would probably use something like code below. It is not the fastest method, however it should work with many different formats. It preserves number of char and position of dot too.

#include <stdio.h>

void fixprint(char *s)
{
        size_t i;
        i = 1;
        while (s[i]=='0' || s[i]==' ' || s[i]=='+' || s[i]=='-') {
                if (s[i]=='0') s[i]=' ';
                i++;
        }
}

int main()
{
        float x = .23;
        char s[14];
        sprintf(s,"% 8.2f",x);
        fixprint(s);
        printf("%s\n",s);
}


You can not do this using printf() So how can you achieve this perfectly?

Here is my solution.

sprintf() => to convert float to string.

    #include <stdio.h>
    #include <string.h>
    int main()
    {
    char result[50];
    float num = 0.23;
    sprintf(result, "%.2f", num);

    char *str = result;
    int n = strspn(str, "0" );
    printf("Trimmed string is %s ", &str[n]);

    return 0;
}

Output

Trimmed string is .23
0

精彩评论

暂无评论...
验证码 换一张
取 消