Как скрыть ведущий ноль в printf - PullRequest
23 голосов
/ 08 апреля 2010

Следующие выходы 0.23. Как мне заставить его просто вывести .23?

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

Ответы [ 7 ]

26 голосов
/ 08 апреля 2010

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

Если появляется знак десятичной точки, перед ним должна быть хотя бы одна цифра.

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

5 голосов
/ 08 апреля 2010

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

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

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

4 голосов
/ 08 апреля 2010
double f = 0.23;

assert(f < 0.995 && f >= 0);  
printf(".%02u\n" , (unsigned)((f + 0.005) * 100));
2 голосов
/ 08 апреля 2010
#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);
}
1 голос
/ 05 июня 2016

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

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

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

  3. пока ты на нем, просто решить проблему навсегда, так что вам никогда не придется возвращаться к это снова (например, не жестко кодировать ширину, точность, идти вперед и сделать версии для начального + электронного формата и т. д.)

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

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

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

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

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

(Обратите внимание, что здесь я использую диалект "венгерской нотации". "D" - это двойное число. "A" - это "массив значений". "Sz" - это строковый буфер с NUL-окончанием, а "psz" - указатель 1. Разница между этими двумя заключается в том, что «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

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

1 голос
/ 22 ноября 2014

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

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

Выход:

.123

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

0 голосов
/ 08 апреля 2010

Похоже, нет простого решения. Я бы, вероятно, использовал что-то вроде кода ниже. Это не самый быстрый метод, однако он должен работать со многими различными форматами. Сохраняет количество символов и положение точки.

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