Using floats with sprintf() in embedded C

2020-02-17 10:01发布

Guys, I want to know if float variables can be used in sprintf() function.

Like, if we write:

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

where adc_read is an integer variable, it will store the string

"adc_read = 1023 \n"

in str (assuming that adc_read = 1023)

How can I use a float variable in place of integer?

13条回答
兄弟一词,经得起流年.
2楼-- · 2020-02-17 10:32

Isn't something like this really easier:

#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楼-- · 2020-02-17 10:37

Many embedded systems have a limited snprintf function that doesn't handle floats. I wrote this, and it does the trick fairly efficiently. I chose to use 64-bit unsigned integers to be able to handle large floats, so feel free to reduce them down to 16-bit or whatever needs you may have with limited resources.

#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 );
}
查看更多
ゆ 、 Hurt°
4楼-- · 2020-02-17 10:41

Yes of course, there is nothing special with floats. You can use the format strings as you use in printf() for floats and anyother datatypes.

EDIT I tried this sample code:

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

Output was : Test=0.61

查看更多
5楼-- · 2020-02-17 10:43

%g can do this:

#include <stdio.h>
int main() {
  float w = 234.567;
  char x[__SIZEOF_FLOAT__];
  sprintf(x, "%g", w);
  puts(x);
}
查看更多
手持菜刀,她持情操
6楼-- · 2020-02-17 10:44

Look in the documentation for sprintf for your platform. Its usually %f or %e. The only place you will find a definite answer is the documentation... if its undocumented all you can do then is contact the supplier.

What platform is it? Someone might already know where the docs are... :)

查看更多
萌系小妹纸
7楼-- · 2020-02-17 10:45

Since you're on an embedded platform, it's quite possible that you don't have the full range of capabilities from the printf()-style functions.

Assuming you have floats at all (still not necessarily a given for embedded stuff), you can emulate it with something like:

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);

You'll need to restrict how many characters come after the decimal based on the sizes of your integers. For example, with a 16-bit signed integer, you're limited to four digits (9,999 is the largest power-of-ten-minus-one that can be represented).

However, there are ways to handle this by further processing the fractional part, shifting it by four decimal digits each time (and using/subtracting the integer part) until you have the precision you desire.


Update:

One final point you mentioned that you were using avr-gcc in a response to one of the other answers. I found the following web page that seems to describe what you need to do to use %f in your printf() statements here.

As I originally suspected, you need to do some extra legwork to get floating point support. This is because embedded stuff rarely needs floating point (at least none of the stuff I've ever done). It involves setting extra parameters in your makefile and linking with extra libraries.

However, that's likely to increase your code size quite a bit due to the need to handle general output formats. If you can restrict your float outputs to 4 decimal places or less, I'd suggest turning my code into a function and just using that - it's likely to take up far less room.

In case that link ever disappears, what you have to do is ensure that your gcc command has "-Wl,-u,vfprintf -lprintf_flt -lm". This translates to:

  • force vfprintf to be initially undefined (so that the linker has to resolve it).
  • specify the floating point printf() library for searching.
  • specify the math library for searching.
查看更多
登录 后发表回答