Convert float to string without sprintf()
Solution 1
Try this. It should be nice and small. I've output the string directly - doing a printf, rather than a sprintf. I'll leave it to you to allocate space for the return string, as well as copying the result into it.
// prints a number with 2 digits following the decimal place
// creates the string backwards, before printing it character-by-character from
// the end to the start
//
// Usage: myPrintf(270.458)
// Output: 270.45
void myPrintf(float fVal)
{
char result[100];
int dVal, dec, i;
fVal += 0.005; // added after a comment from Matt McNabb, see below.
dVal = fVal;
dec = (int)(fVal * 100) % 100;
memset(result, 0, 100);
result[0] = (dec % 10) + '0';
result[1] = (dec / 10) + '0';
result[2] = '.';
i = 3;
while (dVal > 0)
{
result[i] = (dVal % 10) + '0';
dVal /= 10;
i++;
}
for (i=strlen(result)-1; i>=0; i--)
putc(result[i], stdout);
}
Solution 2
Here's a version optimized for embedded systems that doesn't require any stdio or memset, and has low memory footprint. You're responsible for passing a char buffer initialized with zeros (with pointer p
) where you want to store your string, and defining CHAR_BUFF_SIZE
when you make said buffer (so the returned string will be null terminated).
static char * _float_to_char(float x, char *p) {
char *s = p + CHAR_BUFF_SIZE; // go to end of buffer
uint16_t decimals; // variable to store the decimals
int units; // variable to store the units (part to left of decimal place)
if (x < 0) { // take care of negative numbers
decimals = (int)(x * -100) % 100; // make 1000 for 3 decimals etc.
units = (int)(-1 * x);
} else { // positive numbers
decimals = (int)(x * 100) % 100;
units = (int)x;
}
*--s = (decimals % 10) + '0';
decimals /= 10; // repeat for as many decimal places as you need
*--s = (decimals % 10) + '0';
*--s = '.';
while (units > 0) {
*--s = (units % 10) + '0';
units /= 10;
}
if (x < 0) *--s = '-'; // unary minus sign for negative numbers
return s;
}
Tested on ARM Cortex M0 & M4. Rounds correctly.
Solution 3
// convert float to string one decimal digit at a time
// assumes float is < 65536 and ARRAYSIZE is big enough
// problem: it truncates numbers at size without rounding
// str is a char array to hold the result, float is the number to convert
// size is the number of decimal digits you want
void FloatToStringNew(char *str, float f, char size)
{
char pos; // position in string
char len; // length of decimal part of result
char* curr; // temp holder for next digit
int value; // decimal digit(s) to convert
pos = 0; // initialize pos, just to be sure
value = (int)f; // truncate the floating point number
itoa(value,str); // this is kinda dangerous depending on the length of str
// now str array has the digits before the decimal
if (f < 0 ) // handle negative numbers
{
f *= -1;
value *= -1;
}
len = strlen(str); // find out how big the integer part was
pos = len; // position the pointer to the end of the integer part
str[pos++] = '.'; // add decimal point to string
while(pos < (size + len + 1) ) // process remaining digits
{
f = f - (float)value; // hack off the whole part of the number
f *= 10; // move next digit over
value = (int)f; // get next digit
itoa(value, curr); // convert digit to string
str[pos++] = *curr; // add digit to result string and increment pointer
}
}
Solution 4
I can't comment on enhzflep's response, but to handle negative numbers correctly (which the current version does not), you only need to add
if (fVal < 0) {
putc('-', stdout);
fVal = -fVal;
}
at the beginning of the function.
Solution 5
While you guys were answering I've come up with my own solution which that works better for my application and I figure I'd share. It doesn't convert the float to a string, but rather 8-bit integers. My range of numbers is very small (0-15) and always non-negative, so this will allow me to send the data over bluetooth to my android app.
//Assumes bytes* is at least 2-bytes long
void floatToBytes(byte_t* bytes, float flt)
{
bytes[1] = (byte_t) flt; //truncate whole numbers
flt = (flt - bytes[1])*100; //remove whole part of flt and shift 2 places over
bytes[0] = (byte_t) flt; //truncate the fractional part from the new "whole" part
}
//Example: 144.2345 -> bytes[1] = 144; -> bytes[0] = 23
audiFanatic
Updated on January 05, 2022Comments
-
audiFanatic over 2 years
I'm coding for a microcontroller-based application and I need to convert a
float
to a character string, but I do not need the heavy overhead associated with sprintf(). Is there any eloquent way to do this? I don't need too much. I only need 2 digits of precision. -
M.M about 10 yearsPerhaps add
0.005
tofVal
at the start; that way you don't end up with things like 269.9999834 being printed as269.99
instead of270.00
. -
chux - Reinstate Monica about 10 yearsNote: This loses part of the "2 digits precision" as directed in the post. Simple fix:
flt += 0.005
first. -
Beta Decay over 9 years@chux What would I do if I wanted 9 digit precision?
-
chux - Reinstate Monica over 9 years@user12321 The quick answer is
flt += 1e-9/2; ... flt = (flt - bytes[1])*1e9;
but other issues like integer math overflow may come into play elsewhere. Better to craft an answer after you provide detail of your desired range and type of expected input/output. -
Murat Şeker almost 7 yearsabsbuffer is not deallocated
-
chux - Reinstate Monica about 6 years
(int)(fVal * 100)
readily failsint
conversion due to being out of range. Yet it appears OP is not concerned about that. -
Antti Haapala -- Слава Україні almost 5 yearsindeed, you should compare the value against
INT_MAX/100
andINT_MIN/100
before... unless the range is guaranteed to be within -
Joshua Webb over 4 yearsNote: this will convert
0
to.00
, if you want0.00
, use ado..while
instead -
Ingmar over 3 yearsTwo digits after the dot - OK. I needed a quick solution since printf() does not work with my CubeIDE project, although I set -u _printf_float and the linker script seems to be OK. Your functions suits my needs as I only needed a rough float output on the debug console to see whether the value is there. Awesome and thank you.