Подтвердить что ты не робот

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

double f = 0.23;

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

Ответ 4

#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

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

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

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

  • пока вы на нем, просто решить проблему навсегда, чтобы вам никогда не приходилось возвращаться он снова (например, не ограничивайте ширину, точность, сделайте версии для ввода-вывода, e-format и т.д.)

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

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

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

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

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

(Примечание: здесь я использую диалект "Венгерской нотации". "d" - это двойной. "a" - это "массив". "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() 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

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

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

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

Вывод:

.123

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