Как скрыть ноль в printf

следующие мероприятия 0.23. Как мне получить его просто output .23?

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

7 ответов


стандарт C говорит, что для f и F спецификаторы формата с плавающей запятой:

если появляется десятичный символ, перед ним появляется по крайней мере одна цифра.

Я думаю, что если вы не хотите, чтобы ноль появился перед десятичной запятой, вам, вероятно, придется сделать что-то вроде use snprintf() чтобы отформатировать число в строку и удалить 0 Если отформатированная строка начинается с "0."(и аналогично для "-0."). Затем проходить это форматированная строка для нашего реального вывода. Или что-то в этом роде.


это невозможно сделать только с помощью 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.

Примечание если появляется десятичная точка, перед ней появляется по крайней мере одна цифра.

поэтому, похоже, вам придется handcode собственный форматер.


double f = 0.23;

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

#include <stdio.h>

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

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

стандартная библиотека C не предоставляет, поэтому вы должны написать его сами. Это не редкое, одноразовое требование. Рано или поздно вам нужно будет написать аналогичные функции, чтобы обрезать конечные нули и добавить тысячи разделителей. Таким образом, стоит не только получить байты вывода, которые вы ищете, но и проиллюстрировать в более общем виде, как писать сильную библиотеку. При этом имейте в виду:

  1. выясните, как вы хотите это назвать. Что-то вроде этого ты пишешь один раз, но звонишь. миллион раз, так что сделай вызов как можно проще.

  2. затем сделайте набор тестов упражнение все альтернативы вы можете думать о

  3. пока вы на нем, просто реши проблему навсегда, чтобы тебе никогда не пришлось возвращаться. это снова (например, не hardcode ширина, точность, идти вперед и сделать версии для leading-plus, e-format и так далее)

  4. сделать это поточно-даже если вы не используете потоки (конкретный случай пункт 3, На самом деле)

таким образом, работа в обратном направлении: безопасность потоков требует выделения хранилища в стеке, что должно быть сделано от вызывающего абонента. Это не красиво и не весело, просто привыкай к этому. Это с пути. Форматы могут иметь ширину, точность, некоторые флаги и тип преобразования (f, e, g). Поэтому давайте сделаем параметры ширины и точности. Вместо полной параметризации публичного API у меня будет несколько точек входа, которые говорят в имя функции, флаги и тип преобразования, которые они используют.

Pet peeve-это то, что при передаче буферов в функции функция должна знать размер. Но если вы сделаете это отдельным параметром, это боль в но как 1) вызывающий должен написать его и 2) вызывающий может получить его неправильно. Поэтому мой личный стиль-сделать макрос маскировки, который предполагает, что буфер является массивом символов, а не указателем, и использует sizeof() для передачи размера в более подробную версию функция принимает размер.

вот макет самого простого способа, который я могу придумать, чтобы назвать его, с тестовыми случаями.

(Note COUNT () - это макрос, который я использовал еженедельно в течение десятилетий, чтобы получить количество элементов в массиве. Стандарт С должен был иметь что-то вроде этого.)

(обратите внимание, что я использую диалект "венгерской нотации" здесь. "д" - это двойник. "а "есть" массив.""sz"-это строковый буфер с нулевым завершением, а " 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 ] = '';

  // _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, '' );

  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

дополнительное тестирование должно проверить, что функции работают с буферами, которые слишком малы.


похоже, нет простого решения. Вероятно, я бы использовал что-то вроде кода ниже. Это не самый быстрый метод, однако он должен работать со многими различными форматами. Он сохраняет ряд 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);
}

просто преобразовать его в целое число с требуемой точностью

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

выход:

.123

пример источника на pastebin