views:

2788

answers:

13

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?

+1  A: 

use the %f modifier:

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

For instance:

#include <stdio.h>

int main (void) 
{
    float x = 2.5;
    char y[200];

    sprintf(y, "x = %f\n", x);
    printf(y);
    return 0;
}

Yields this:

x = 2.500000

Nathan Fellman
I did, but it didn't work it prints a ? character instead of the string
aditya
A: 

Yes, use %f

rein
I did, but it didn't work
aditya
it prints a ? character instead of the string.
aditya
How didn't it work? Wrong output?
rein
ya. its showing ? character
aditya
Did you properly allocate space for str?
Scarlet
+1  A: 

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

Naveen
I did, but it didn't work it prints a ? character instead of the string
aditya
See the edit for my sample code.. I have a feeling that your buffer "str" is too small..try using something similar %.2f to set the precision.
Naveen
size of my buffer is 100actually, i'm printing it on serial port, so i think problem is in transmitting float variables over UART
aditya
@aditya, sprintf (if available) will generate standard ASCII so it's not a problem with the output. More likely it's a limitation of your embedded platform.
paxdiablo
ya, i think so too
aditya
+2  A: 

Yes you can. However, it depends on the C-library that you are linking against and you need to be aware of the consequences.

Since you are programming for embedded applications, realise that floating-point support is emulated for a lot of embedded architectures. Compiling in this floating-point support will end up increasing the size of your executable significantly.

sybreon
i'm using avr-gcc
aditya
+1  A: 

Don't expect sprintf (or any other function with varargs) to automatically cast anything. The compiler doesn't try to read the format string and do the cast for you; at runtime, sprintf has no meta-information available to determine what is on the stack; it just pops bytes and interprets them as given by the format string. sprintf(myvar, "%0", 0); immediately segfaults.

So: The format strings and the other arguments must match!

ammoQ
+8  A: 

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;

int d1 = adc_read;            // Get the integer part (678).
float f2 = adc_read - d1;     // Get fractional part (678.0123 - 678 = 0.0123).
int d2 = trunc(f2 * 10000);   // Turn into integer (123).
//int d2 = (int)(f2 * 10000); // Or this one: Turn into integer.

// Print as parts, note that you need 0-padding for fractional bit.
// Since d1 is 678 and d2 is 123, you get "678.0123".
sprintf (str, "adc_read = %d.%04d\n", d1, d2);

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, you could use repeated fractional bits to get more precision as follows:

char str[100];
float adc_read = 678.01234567;

int d1 = adc_read;            // Get the integer part (678).
float f2 = adc_read - d1;     // Get fractional part (0.01234567).
int d2 = trunc(f2 * 10000);   // Turn into integer (123).
float f3 = f2 * 10000 - d2;   // Get next fractional part (0.4567).
int d3 = trunc(f3 * 10000);   // Turn into integer (4567).

sprintf (str, "adc_read = %d.%04d%04d\n", d1, d2, d3);

This should work up to the precision of your floats (and you may want to make it a library function so it doesn't look so ugly within the main flow of code).

Check the docs for your embedded platform. Either floats aren't supported by printf() (likely) or you have to link in some special code.

Update:

One final point, aditya, 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.
paxdiablo
that's a nice trick, i'll try it outthanx
aditya
it's giving compilation errorerror: expected expression before ‘int’in above code in line 5.
aditya
Sorry, too much Java. See update.
paxdiablo
what ??? i didn't get it
aditya
The edit worked perfectly, thanks Paxplease explain
aditya
@aditya, have a look at the comments, they now show how the numbers are extracted.
paxdiablo
Note that manipulating a float by subtracting out the integer portion and multiplying by 10000 to get the fraction part may change the value of some of the least significant digits
Trent
+1  A: 

Yes, and no. Despite what some other replies have said, the C compiler is required to perform conversions for sprintf(), and all other variadic functions, as follows:

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

(and signed/unsigned variants of the above integral types)

It does this precisely because sprintf() (and the other print()-family functions) would be unusable without it. (Of course, they're pretty unusable as it is.)

But you cannot assume any other conversions, and your code will have undefined behaviour - read: crash! - if you do it.

dcw
The compiler is required to perform _conversions_. A _cast_ is explicitly requested by the programmer.
mlp
fair comment. fixing now
dcw
"Unusable" is a pretty strong word. The printf() family is quite usable, but it is not perfect. It has obvious type-safety flaws, but the compiler can easily check the types of the arguments you're passing in 99% of use cases and give you a warning/error (see gcc's -Wformat et al).
Adam Rosenfield
Adam: "99%" is also a pretty emphatic claim, =P. _Some_ compilers can do that check, but are only able to do so when the format parameter is a string literal included in the printf() statement. If the format string is obtained from elsewhere, or synthesised in situ, then the compiler is of no help.
dcw
A: 

well' i'm still searching the answer, but thanks a lot guys

aditya
A: 

Tha edit worked perfectly, thanks Pax Please explain

aditya
You should edit your original post explaining what you've changed (such as: "it only prints a ? when using %f" and things), not new answers.
GMan
A: 

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... :)

jheriko
it's Atmega128 microcontrollerThe compiler is avr-gcc.Thanks for the reply, but the answer given by Pax worked perfectly.
aditya
A: 

Question :

Input : PrintFloat(0); PrintFloat(68); PrintFloat(68.1); PrintFloat(68.9999999999999999); PrintFloat(68.3); PrintFloat(678.01234567); PrintFloat(0.0); PrintFloat(0.1); PrintFloat(0.0000001);

void PrintFloat(float value ) {

char str[100];
int d1 = value;            // Get the integer part (678).
float f2 = value - d1;     // Get fractional part (0.01234567).
int d2 = (int)(f2 * 10000);   // Turn into integer (123).
float f3 = f2 * 10000 - d2;   // Get next fractional part (0.4567).
int d3 = (int)(f3 * 10000);   // Turn into integer (4567).

sprintf(str, "Float value = %d.%04d%04d\n", d1, d2,d3);
printf("%s", str);

}

The o/p is :

       68.00000000
       68.09999847
       69.00000000
       68.30000305
       678.01232910
         0.00000000
         0.10000000
         0.00000010

Please anybody clarify why o/p is not as expected??How to solve this issue?

A: 
char str[100]; 
int d1 = value;

Better to use

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

Then you wont get rounding up of the integer part (68.9999999999999999 becomes 69.00..). 68.09999847 instead of 68.1 is difficult to avoid - any floating point format has limited precision.

setltd
A: 

I faced the same trouble. In my case '(float)' was appeared in the output for %f. Once I've added 'FLOAT = ieee' directive to my Makefile it works fine.

Roman