代码之家  ›  专栏  ›  技术社区  ›  Christian Mann

printf中如何隐藏前导零

  •  23
  • Christian Mann  · 技术社区  · 16 年前

    0.23 . 如何让它简单地输出 .23 ?

    printf( "%8.2f" , .23 );
    
    7 回复  |  直到 10 年前
        1
  •  28
  •   Michael Burr    16 年前

    f F 浮点格式说明符:

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

    我认为如果你不想在小数点之前出现一个0,你可能需要做一些类似于use的事情 snprintf() 将数字格式化为字符串,并删除 0

        2
  •  6
  •   Peter Mortensen Pieter Jan Bonestroo    14 年前

    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.
    

    注意 .

        3
  •  4
  •   jfs    10 年前
    double f = 0.23;
    
    assert(f < 0.995 && f >= 0);  
    printf(".%02u\n" , (unsigned)((f + 0.005) * 100));
    
        4
  •  2
  •   Michael Burr    16 年前
    #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);
    }
    
        5
  •  2
  •   Lightness Races in Orbit    10 年前

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

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

    输出:

    .123
    

    example source on pastebin

        6
  •  1
  •   Swiss Frank    10 年前

    标准C库不提供这个,所以你必须自己写。这不是罕见的一次性要求。您迟早需要编写类似的函数来修剪尾随的零并添加数千个分隔符。因此,不仅要获得所需的输出字节,还要更全面地说明如何编写强大的库。进行此操作时,请记住:

    1. 弄清楚你想怎么称呼它。像这样的东西你只写过一次 一百万次,所以打电话要尽可能容易。

    2. 锻炼你能想到的所有选择

    3. 永远解决这个问题,这样你就不必再回到过去 再重复一次(例如,不要硬编码宽度、精度,继续并使

    4. 成功 线程安全,即使您不使用线程(一个特定的 第三点,实际上)

    所以向后工作:线程安全需要在堆栈上分配存储,这必须从调用方完成。这既不好看也不好玩,但要习惯。这是C路。格式可以有宽度、精度、一些标志和转换类型(f、e、g)。所以让我们来设置宽度和精度参数。与其完全参数化公共API,不如在函数名中设置多个入口点,说明它们使用的标志和转换类型。

    令人恼火的是,当将缓冲区传递给函数时,函数需要知道缓冲区的大小。但是如果你把它作为一个单独的参数,这是一个痛苦的过程,但是1)调用者必须编写它,2)调用者可能会弄错它。因此,我个人的风格是制作一个掩蔽宏,该宏假定缓冲区是字符数组,而不是指针,并使用sizeof()将大小传递给获取大小的函数的更详细版本。

    (Note COUNT()是我几十年来每周使用的一个宏,用于获取数组中元素的数量。标准C,应该是这样的。)

    (注:我这里用的是“匈牙利语符号”的方言“d”是双精度的“sz”是以NUL结尾的字符串缓冲区,而“psz”是指向一个字符串缓冲区的指针。这两者之间的区别在于,“sz”可以与COUNT()或sizeof()一起使用以获取数组大小,而“psz”则不能i”是一个整数,特定变量“i”用于循环。

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

    现在,“f”和“+f”版本看起来非常相似,所以让它们都调用一个内部函数。下面是函数,它们接受缓冲区大小,宏自己计算出来(并行函数也是为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
    

    额外的测试应该验证函数是否与太小的缓冲区一起工作。

        7
  •  0
  •   Michas    16 年前

    看来没有简单的解决办法。我可能会使用下面的代码。它不是最快的方法,但是它应该适用于许多不同的格式。它还保留了字符的个数和点的位置。

    #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);
    }
    
        8
  •  0
  •   Shoaib Kakal    6 年前

    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