Использование float с sprintf() во встроенном C
Ребята, я хочу знать, могут ли переменные float
использоваться в sprintf()
.
Например, если мы пишем:
sprintf(str,"adc_read = %d \n",adc_read);
где adc_read
- целочисленная переменная, она сохранит строку
"adc_read = 1023 \n"
в str
(предполагая, что adc_read = 1023
)
Как я могу использовать переменную float вместо целого?
Ответы
Ответ 1
Поскольку вы работаете на встроенной платформе, вполне возможно, что вы не обладаете полным спектром возможностей функций printf()
-style.
Предполагая, что у вас вообще есть плавающие объекты (все еще не обязательно заданные для встроенных объектов), вы можете эмулировать их примерно так:
char str[100];
float adc_read = 678.0123;
char *tmpSign = (adc_read < 0) ? "-" : "";
float tmpVal = (adc_read < 0) ? -adc_read : adc_read;
int tmpInt1 = tmpVal; // Get the integer (678).
float tmpFrac = tmpVal - tmpInt1; // Get fraction (0.0123).
int tmpInt2 = trunc(tmpFrac * 10000); // Turn into integer (123).
// Print as parts, note that you need 0-padding for fractional bit.
sprintf (str, "adc_read = %s%d.%04d\n", tmpSign, tmpInt1, tmpInt2);
Вам нужно будет ограничить количество символов после десятичного знака в зависимости от размеров ваших целых чисел. Например, с 16-разрядным целым числом со знаком вы ограничены четырьмя цифрами (9 999 - это наибольшая степень десяти-минус единицы, которую можно представить).
Однако есть способы справиться с этим, дополнительно обрабатывая дробную часть, каждый раз сдвигая ее на четыре десятичных знака (и используя/вычитая целую часть), пока вы не получите желаемую точность.
Обновить:
В заключение вы упомянули, что вы использовали avr-gcc
в ответе на один из других ответов. Я нашел следующую веб-страницу, которая, кажется, описывает, что вам нужно сделать, чтобы использовать %f
в ваших выражениях printf()
здесь.
Как я изначально подозревал, вам нужно проделать дополнительную работу, чтобы получить поддержку с плавающей запятой. Это потому, что встроенному материалу редко нужна плавающая точка (по крайней мере, ни один из того, что я когда-либо делал). Он включает в себя установку дополнительных параметров в вашем make файле и связывание с дополнительными библиотеками.
Однако, это может немного увеличить размер вашего кода из-за необходимости обрабатывать общие выходные форматы. Если вы можете ограничить число выводов с плавающей запятой до 4 десятичных разрядов или меньше, я бы предложил превратить мой код в функцию и просто использовать ее - это может занять гораздо меньше места.
В случае, если эта ссылка когда-либо исчезнет, вам нужно убедиться, что ваша команда gcc имеет "-Wl,-u,vfprintf -lprintf_flt -lm
". Это переводится как:
- заставить vfprintf быть изначально неопределенным (чтобы компоновщик должен был его разрешить).
- указать библиотеку
printf()
с плавающей точкой для поиска. - указать математическую библиотеку для поиска.
Ответ 2
Разве это не так проще:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char str[10];
float adc_read = 678.0123;
dtostrf( adc_read, 3, 4, temp );
sprintf(str,"adc_read = %10s \n", temp);
printf(temp);
Ответ 3
Да, вы можете. Однако это зависит от C-библиотеки, с которой вы связываетесь, и вам нужно знать о последствиях.
Поскольку вы программируете для встроенных приложений, понимайте, что поддержка с плавающей запятой эмулируется для множества встроенных архитектур. Компиляция в этой поддержке с плавающей запятой приведет к значительному увеличению размера вашего исполняемого файла.
Ответ 4
Не ожидайте, что sprintf (или любая другая функция с varargs) автоматически добавит что-либо. Компилятор не пытается прочитать строку формата и сделать бросок для вас; во время выполнения sprintf не имеет метаинформации, доступной для определения того, что находится в стеке; он просто выдает байты и интерпретирует их как заданные строкой формата. sprintf (myvar, "% 0", 0); немедленно segfaults.
Итак: Строки формата и другие аргументы должны соответствовать!
Ответ 5
используйте модификатор %f
:
sprintf (str, "adc_read = %f\n", adc_read);
Например:
#include <stdio.h>
int main (void)
{
float x = 2.5;
char y[200];
sprintf(y, "x = %f\n", x);
printf(y);
return 0;
}
Уступает:
x = 2.500000
Ответ 6
Да, конечно, нет ничего особенного в поплавках. Вы можете использовать строки формата, которые используются в printf() для float и любых других типов данных.
ИЗМЕНИТЬ
Я попробовал этот пример кода:
float x = 0.61;
char buf[10];
sprintf(buf, "Test=%.2f", x);
printf(buf);
Выход был: Test = 0.61
Ответ 7
Да, и нет. Несмотря на то, что говорили некоторые другие ответы, компилятор C требуется для выполнения преобразований для sprintf()
и всех других вариационных функций следующим образом:
-
char
= > int
-
short
= > int
-
float
= > double
(и подписанные/неподписанные варианты указанных интегральных типов)
Он делает это именно потому, что sprintf()
(и другие print()
-семейные функции) без него будет непригодным. (Конечно, они довольно непригодны для использования.
Но вы не можете предполагать никаких других преобразований, и ваш код будет иметь поведение undefined - читать: crash! - если вы это сделаете.
Ответ 8
Посмотрите документацию для sprintf для своей платформы. Обычно он% f или% e. Единственное место, где вы найдете определенный ответ, - это документация... если его недокументированное все, что вы можете сделать, это связаться с поставщиком.
Какая платформа? Кто-то может уже знать, где документы...:)
Ответ 9
Не делайте этого; целые числа в C/С++ всегда округляются, поэтому нет необходимости использовать функцию floor.
char str[100];
int d1 = value;
Лучше использовать
int d1 = (int)(floor(value));
Тогда вы не получите округление целой части (68.9999999999999999 становится 69.00..).
68.09999847 вместо 68.1 трудно избежать - любой формат с плавающей запятой имеет ограниченную точность.
Ответ 10
% g может сделать это:
#include <stdio.h>
int main() {
float w = 234.567;
char x[__SIZEOF_FLOAT__];
sprintf(x, "%g", w);
puts(x);
}
Ответ 11
Многие встроенные системы имеют ограниченную функцию snprintf, которая не обрабатывает операции с плавающей точкой. Я написал это, и это довольно эффективно. Я решил использовать 64-разрядные целые числа без знака, чтобы иметь возможность обрабатывать большие числа с плавающей запятой, поэтому не стесняйтесь сокращать их до 16-разрядных или любых других потребностей с ограниченными ресурсами.
#include <stdio.h> // for uint64_t support.
int snprintf_fp( char destination[], size_t available_chars, int decimal_digits,
char tail[], float source_number )
{
int chars_used = 0; // This will be returned.
if ( available_chars > 0 )
{
// Handle a negative sign.
if ( source_number < 0 )
{
// Make it positive
source_number = 0 - source_number;
destination[ 0 ] = '-';
++chars_used;
}
// Handle rounding
uint64_t zeros = 1;
for ( int i = decimal_digits; i > 0; --i )
zeros *= 10;
uint64_t source_num = (uint64_t)( ( source_number * (float)zeros ) + 0.5f );
// Determine sliding divider max position.
uint64_t div_amount = zeros; // Give it a head start
while ( ( div_amount * 10 ) <= source_num )
div_amount *= 10;
// Process the digits
while ( div_amount > 0 )
{
uint64_t whole_number = source_num / div_amount;
if ( chars_used < (int)available_chars )
{
destination[ chars_used ] = '0' + (char)whole_number;
++chars_used;
if ( ( div_amount == zeros ) && ( zeros > 1 ) )
{
destination[ chars_used ] = '.';
++chars_used;
}
}
source_num -= ( whole_number * div_amount );
div_amount /= 10;
}
// Store the zero.
destination[ chars_used ] = 0;
// See if a tail was specified.
size_t tail_len = strlen( tail );
if ( ( tail_len > 0 ) && ( tail_len + chars_used < available_chars ) )
{
for ( size_t i = 0; i <= tail_len; ++i )
destination[ chars_used + i ] = tail[ i ];
chars_used += tail_len;
}
}
return chars_used;
}
main()
{
#define TEMP_BUFFER_SIZE 30
char temp_buffer[ TEMP_BUFFER_SIZE ];
char degrees_c[] = { (char)248, 'C', 0 };
float float_temperature = 26.845f;
int len = snprintf_fp( temp_buffer, TEMP_BUFFER_SIZE, 2, degrees_c, float_temperature );
}
Ответ 12
Да, используйте% f