Стандартная библиотека C не предоставляет этого, поэтому вы должны написать ее самостоятельно. Это не редкое, разовое требование. Вам придется рано или поздно написать подобные функции, чтобы обрезать конечные нули и добавить тысячи разделителей. Таким образом, стоит не просто получить искомые байты, но и проиллюстрировать, как написать сильную библиотеку. При этом имейте в виду:
выясните, как вы хотите это назвать. Примерно так ты пишешь один раз, но звонишь
миллион раз, поэтому сделайте вызов как можно проще.
тогда сделайте набор тестов
используя все возможные альтернативы
пока ты на нем,
просто решить проблему навсегда, так что вам никогда не придется возвращаться к
это снова (например, не жестко кодировать ширину, точность, идти вперед и сделать
версии для начального + электронного формата и т. д.)
сделай это
потокобезопасен, даже если вы не используете потоки (конкретный случай
пункт 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
Дополнительное тестирование должно проверить, что функции работают с слишком маленькими буферами.