如何隐藏printf中的前导零

以下输出0.23 。 如何让它简单输出.23

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

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

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

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

仅使用printf是不可能的。 printf的文件说:

 f - "double" argument is output in conventional form, ie [-]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. 

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

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

 double f = 0.23; assert(f < 0.995 && f >= 0); printf(".%02u\n" , (unsigned)((f + 0.005) * 100)); 
 #include  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); } 

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

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

输出:

 .123 

在pastebin上的示例源

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

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

  2. 然后让测试套件运用您能想到的所有替代方案

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

  4. 即使你没有使用线程也使它成为线程安全的(实际上是第3点的特定情况)

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

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

这是我可以想到的最简单的方法模型,用测试用例。

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

(注意我在这里使用“匈牙利表示法”的方言。“d”是双精度。“a”是“数组”。“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 ) ) 

最后是完成工作的(内部)function。 请注意,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 

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

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

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