Использование float с sprintf () во встроенном C - PullRequest
25 голосов
/ 25 мая 2009

Ребята, я хочу знать, можно ли использовать float переменные в функции sprintf().

Как, если мы напишем:

sprintf(str,"adc_read = %d \n",adc_read);

где adc_read - целочисленная переменная, в ней будет храниться строка

"adc_read = 1023 \n"

в str (при условии, что adc_read = 1023)

Как я могу использовать переменную с плавающей точкой вместо целого числа?

Ответы [ 12 ]

47 голосов
/ 25 мая 2009

Поскольку вы работаете на встроенной платформе, вполне возможно, что вы не обладаете полным спектром возможностей от функций printf() -тиля.

Предполагая, что у вас вообще есть плавающие объекты (все еще не обязательно заданные для встроенных объектов), вы можете эмулировать их примерно так:

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() для поиска.
  • указать математическую библиотеку для поиска.
3 голосов
/ 25 мая 2012

Разве не так просто:

#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);
2 голосов
/ 25 мая 2009

Да, вы можете. Однако это зависит от C-библиотеки, с которой вы связываетесь, и вам необходимо знать о последствиях.

Поскольку вы программируете для встроенных приложений, имейте в виду, что поддержка с плавающей запятой эмулируется для многих встроенных архитектур. Компиляция в этой поддержке с плавающей точкой приведет к значительному увеличению размера исполняемого файла.

1 голос
/ 25 мая 2009

Не ожидайте, что sprintf (или любая другая функция с varargs) автоматически приведёт что-либо. Компилятор не пытается прочитать строку формата и выполнить приведение за вас; во время выполнения sprintf не имеет доступной мета-информации для определения того, что находится в стеке; он просто извлекает байты и интерпретирует их в соответствии со строкой формата. sprintf (myvar, "% 0", 0); немедленно segfaults.

Итак: строки формата и другие аргументы должны совпадать!

0 голосов
/ 15 марта 2019

Многие встроенные системы имеют ограниченную функцию 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 );
}
0 голосов
/ 12 января 2016

% г может сделать это:

#include <stdio.h>
int main() {
  float w = 234.567;
  char x[__SIZEOF_FLOAT__];
  sprintf(x, "%g", w);
  puts(x);
}
0 голосов
/ 31 декабря 2009

Не делай этого; целые числа в C / C ++ всегда округляются, поэтому нет необходимости использовать функцию floor.

char str[100]; 
int d1 = value;

Лучше использовать

int d1 = (int)(floor(value));

Тогда вы не получите округления до целой части (68.9999999999999999 становится 69.00 ..). Трудно избежать 68.09999847 вместо 68.1 - любой формат с плавающей запятой имеет ограниченную точность.

0 голосов
/ 25 мая 2009

Посмотрите в документации по sprintf для вашей платформы. Обычно это% f или% e. Единственное место, где вы найдете конкретный ответ, - это документация ... если ее нет, все, что вы можете сделать, - это связаться с поставщиком.

Какая это платформа? Кто-то, возможно, уже знает, где находятся документы ...:)

0 голосов
/ 25 мая 2009

Да, и нет. Несмотря на то, что сказали некоторые другие ответы, компилятор C необходим для выполнения преобразований для sprintf() и всех других функций с переменными, как указано ниже:

  • char => int
  • short => int
  • float => double

(и подписанные / беззнаковые варианты вышеуказанных целочисленных типов)

Это происходит именно потому, что sprintf() (и другие print() -семейные функции) были бы бесполезны без него. (Конечно, они довольно непригодны, поскольку .)

Но вы не можете предполагать какие-либо другие преобразования, и ваш код будет иметь неопределенное поведение - читайте: крах! - если ты это сделаешь.

0 голосов
/ 25 мая 2009

Да, конечно, в поплавках нет ничего особенного. Вы можете использовать строки формата, как вы используете в printf () для чисел с плавающей точкой и любых других типов данных.

EDIT Я попробовал этот пример кода:

float x = 0.61;
char buf[10];
sprintf(buf, "Test=%.2f", x);
printf(buf);

Вывод был: Тест = 0,61

...