如何在printf中隐藏前导零

时间:2010-04-07 20:36:33

标签: c double printf

以下输出0.23。如何才能输出.23

printf( "%8.2f" , .23 );

8 个答案:

答案 0 :(得分:26)

C标准表示对于fF浮点格式说明符:

  

如果出现小数点字符,则至少会出现一个数字。

我认为如果你不希望零点出现在小数点之前,你可能需要做一些事情,比如使用snprintf()将数字格式化为字符串,并删除{{1如果格式化的字符串以“0”开头(类似于“-0。”)。然后将格式化的字符串传递给我们的实际输出或类似的东西。

答案 1 :(得分:5)

仅使用printf无法执行此操作。 printf的文档说:

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.

注意如果出现小数点,则至少会出现一个数字

因此,您似乎必须手动编码自己的格式化程序。

答案 2 :(得分:4)

double f = 0.23;

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

答案 3 :(得分:2)

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

答案 4 :(得分:1)

只需将其转换为具有所需精度的整数

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

输出:

.123

example source on pastebin

答案 5 :(得分:1)

标准C库不提供此功能,因此您必须自己编写。这不是一个罕见的一次性要求。您迟早要编写类似的函数来修剪尾随零并添加千位分隔符。因此,不仅要获取您正在寻找的输出字节,而且要更一般地说明如何编写强大的库,这是值得的。这样做时请记住:

  1. 弄清楚你想怎么称呼它。这样的东西,你写一次,但打电话 一百万次,所以尽可能简化调用。

  2. 然后制作测试套件 行使你能想到的所有替代方案

  3. 当你在这里时, 只是永远解决问题所以你永远不必回来 它再次(例如,不要硬编码宽度,精度,继续和制作 领先加,电子格式等的版本)

  4. 制作它 即使你不使用线程也是线程安全的(具体情况是 第3点,实际上)

  5. 所以向后工作:线程安全需要在堆栈上分配存储,这必须从调用者完成。这不是很好或很有趣,但只是习惯了。这是C方式。格式可以有宽度,精度,一些标志和转换类型(f,e,g)。所以我们来制作宽度和精度参数。我没有完全参数化公共API,而是只有多个入口点,在函数名称中说明了它们使用的标志和转换类型。

    一个小小的问题是,当将缓冲区传递给函数时,函数需要知道大小。但是如果你把它作为一个单独的参数,那就很痛苦,但是1)调用者必须写它并且2)调用者可能会弄错。所以我的个人风格是制作一个掩盖宏,假设缓冲区是一个字符数组,而不是一个指针,并使用sizeof()将大小传递给一个更详细的函数版本。

    这里是我用这个测试用例来调用它的最简单方法的模型。

    (注意COUNT()是我每周使用几十年来获取数组中元素数量的宏。标准C应该有这样的东西。)

    (注意我使用方言&#34;匈牙利表示法&#34;这里。&#34; d&#34;是双重的。&#34; a&#34;是&#34;数组。& #34;&#34; sz&#34;是一个以NUL结尾的字符串缓冲区,而&#34; psz&#34;是指向一个的指针。这两者之间的区别在于&#34; sz&#34;可以与COUNT()或sizeof()一起使用以获得数组大小,而&#34; psz&#34;不能。&#34; i&#34;是一个整数和特定变量&#34; i&#34;用于循环。

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

    现在,&#34; f&#34;和&#34; + f&#34;版本似乎非常相似,所以让他们都调用内部函数。以下是获取缓冲区大小的函数,以及自己计算出来的宏。 (并行函数也是为e和g格式编写的。)

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

    最后完成工作的(内部)功能。请注意,snprintf()需要在Windows上使用前置下划线,但在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;
    }
    

    输出结果为:

    .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
    

    其他测试应验证函数是否适用于太小的缓冲区。

答案 6 :(得分:0)

看起来没有简单的解决方案。我可能会使用类似下面的代码。它不是最快的方法,但它应该适用于许多不同的格式。它也保留了char的数量和点的位置。

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

答案 7 :(得分:0)

您无法使用printf()来做到这一点,那么如何才能完美地做到这一点呢?

这是我的解决方案。

sprintf() =>将float转换为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;
}

输出

Trimmed string is .23