Minimizing Memory Consumption on sscanf - c

I have a time&date string acquired from a GSM module.
Here is the time&date format:
"yy/MM/dd,hh:mm:ss±zz"
And an example string:
"+CCLK: "17/04/07,12:57:43+03""
I need to parse it and convert it to Unix Timestamp. I have the below implementation for it and it works well:
Time Struct:
struct timeStruct{
uint16 Year;
uint8 Month;
uint8 Day;
uint8 Hour;
uint8 Minute;
uint8 Second;
};
GSM Clock Command Parser:
void load_clock(void)
{
struct timeStruct gsmTimeStruct;
/* Parse year,month,day,hour,minute,second and GMT timezone */
sscanf(gsm_clk_string, "+CCLK: \"%hd/%d/%d,%d:%d:%d%c%d", &(gsmTimeStruct.Year), &(gsmTimeStruct.Month), &(gsmTimeStruct.Day), &(gsmTimeStruct.Hour), &(gsmTimeStruct.Minute), &(gsmTimeStruct.Second),&CONEZONE,&TIMEZONE);
unixTime = (uint32_t)RTC_DateTimeToUnix(gsmTimeStruct);
/* Collect GMT difference */
if(CONEZONE == '+')
unixTime = unixTime - TIMEZONE*3600;
else
unixTime = unixTime + TIMEZONE*3600;
}
That sscanf function consumes 1776 Bytes from the Flash Memory which is a huge number for this MCU. The Unix conversion function also consumes about 700 Bytes but it is a bit normal as it has some mathematical calculations in it.
Are there any bright ideas on parsing this string without consuming such big flash memory usage?
Note: The error checking for the Command String is not an issue since the GSM module already does it. So, if we want to change the parsing implementation, we don't have to do it. We can just assume those numbers are always there.
Edit: I tried below snippet, it works well. It saved 1.7 kilobytes memory but I'm not sure if this is a proper way.
gsmTimeStruct.Year = (gsm_clk_string[8]-'0')*10 + (gsm_clk_string[9]-'0');
gsmTimeStruct.Month = (gsm_clk_string[11]-'0')*10 + (gsm_clk_string[12]-'0');
gsmTimeStruct.Day = (gsm_clk_string[14]-'0')*10 + (gsm_clk_string[15]-'0');
gsmTimeStruct.Hour = (gsm_clk_string[17]-'0')*10 + (gsm_clk_string[18]-'0');
gsmTimeStruct.Minute = (gsm_clk_string[20]-'0')*10 + (gsm_clk_string[21]-'0');
gsmTimeStruct.Second = (gsm_clk_string[23]-'0')*10 + (gsm_clk_string[24]-'0');
TIMEZONE = (gsm_clk_string[26]-'0')*10 + (gsm_clk_string[27]-'0');
CONEZONE = gsm_clk_string[25];

I would think all you'd need is this, just move the char ptr accordingly.
In code example below i assume the first character in gsm_clk_string is "
/* "+CCLK: "2017/04/07, 12:57:43+03"" */
/* gsm_clk_string[] : 0123456789012345678902134567890123 */
char *ptr;
ptr = gsm_clk_string;
ptr += 9;
gsmTimeStruct.Year = (uint16) ((*ptr - '0') * 1000);
ptr++;
gsmTimeStruct.Year += (uint16) ((*ptr - '0') * 100);
ptr++;
gsmTimeStruct.Year += (uint16) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Year += (uint16) ((*ptr - '0'));
ptr += 2;
gsmTimeStruct.Month = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Month += (uint8) (*ptr - '0');
ptr += 2;
gsmTimeStruct.Day = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Day += (uint8) (*ptr - '0');
ptr += 3;
gsmTimeStruct.Hour = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Hour += (uint8) (*ptr - '0');
ptr += 2;
gsmTimeStruct.Minute = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Minute += (uint8) (*ptr - '0');
ptr += 2;
gsmTimeStruct.Second = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Second += (uint8) (*ptr - '0');
ptr++;
if ( *ptr == '+' )
{
ptr++;
TIMEZONE = (whatever_type_TimeZone) ((*ptr - '0') * 10);
ptr++
TIMEZONE += (whatever_type_TimeZone) ((*ptr - '0'));
unixTime = (uint32_t) RTC_DateTimeToUnix( gsmTimeStruct ) - ( TIMEZONE * 3600 );
}
else if ( *ptr == '-' )
{
ptr++;
TIMEZONE = (whatever_type_TimeZone) ((*ptr - '0') * 10);
ptr++
TIMEZONE += (whatever_type_TimeZone) ((*ptr - '0'));
unixTime = (uint32_t) RTC_DateTimeToUnix( gsmTimeStruct ) + ( TIMEZONE * 3600 );
}
else
{
/* problem */
}

Related

HOW TO MULTIPLY 2 LARGE NUMBERS AS STRINGS(with a previous fanction that adds 2 strings) ? c

verylong multiply_verylong(verylong vl1, verylong vl2)'verylong defines as typedef char* verylong'
{
size_t maxln, minln;
int carry=0, placeSaver=1, i, k ,sum=0,dif,newln,newln2,ln1,ln2;
verylong newNum , maxVl,minVl,tempvl,addvl;
ln1 = strlen(vl1); // 'length of first str'
ln2 = strlen(vl2);'length of second str'
if (ln1 >= ln2)
{
maxln = ln1;
minln = ln2;
maxVl = (verylong)calloc(maxln + 1, sizeof(char));
assert(maxVl);
minVl = (verylong)calloc(minln + 1, sizeof(char));
assert(minVl);
strcpy(maxVl, vl1);
strcpy(minVl, vl2);
dif = maxln - minln;
}
else 'stops debuging here'
{
maxln = ln2;
minln = ln1;
maxVl = (verylong)calloc(maxln + 1, sizeof(char));
assert(maxVl);
minVl = (verylong)calloc(minln + 1, sizeof(char));
assert(minVl);
strcpy(maxVl, vl2);
strcpy(minVl, vl1);
dif = maxln - minln;
}
newln = 2 * maxln + 1; 'maximum length of new required string'
newln2 = newln - 1; 'the index of the new string'
newNum = (verylong)calloc(newln,sizeof(char));
addvl = (verylong)calloc(newln, sizeof(char));
tempvl = (verylong)calloc(newln, sizeof(char));
for (i = minln - 1; i >= 0; i--) ' elementry school multiplication'
{
for (k = maxln - 1; k >= 0; k--)
{
sum = ((minVl[i] - '0')*(maxVl[k] - '0')*placeSaver)+carry;
if (sum >= 10)
carry = sum / 10;
if (k == 0)
newNum[newln2] = '0' + sum;
else
newNum[newln2] = '0' + sum%10;
newln2--;
}
placeSaver*=10;
addvl=add_verylong(newNum,tempvl);'sending the 2 strings to a previous function that adds 2 strings'
strcpy(tempvl, addvl);
}
return addvl;
}
void main()
{
char vl1[80], vl2[80];
printf("enter large number\n");
gets(vl1);
printf("enter large number\n");
gets(vl2);
verylong res = multiply_verylong(vl1, vl2);'saves the string '
printf("%s", res);
free(res);
}
I tried to multiply the first digit of the first number from right with all of the digits from the second number moving forward to the second digit of the first number and then to multiply the placesaver by 10 .
***
the problem is that the code outputs usually nothing and sometimes just inccorect result
***
Your approach is basically right, but there are some mistakes in the implementation.
You allocate space for addvl, but overwrite that pointer later with the return value of add_verylong(). This gives a memory leak. Further memory leaks are produced by not freeing tempvl, maxvl, minvl and newNum.
You forgot to initialize tempvl to the number "0".
We cannot shift the intermediate product by multiplying each digit with the power of ten placeSaver. What we can do is place 0 digits to the right of the product newNum.
You failed to correctly set carry in each loop cycle.
You missed that even the leftmost digits can produce a carry.
This code has the mentioned mistakes corrected:
newNum = calloc(newln, sizeof(char));
// do not allocate space for addvl - it would be lost
tempvl = malloc(newln);
strcpy(tempvl, "0"); // don't forget to initialize tempvl to "0"
for (i = minln - 1; i >= 0; i--) // elementry school multiplication
{
newln2 = newln - 1; // the index of the new string
for (carry = 0, k = maxln - 1; k >= 0; k--) // clear carry before each loop
{
sum = (minVl[i] - '0')*(maxVl[k] - '0') + carry;
carry = sum/10;
newNum[--newln2] = '0' + sum%10;
}
if (carry) newNum[--newln2] = '0' + carry;
addvl = add_verylong(newNum+newln2, tempvl); // number starts at +newln2
free(tempvl); // free obsolete number
tempvl = addvl; // rather than strcpy()
newNum[--newln-1] = '0'; // instead of placeSaver*=10
}
free(maxVl), free(minVl), free(newNum);
return addvl;
It works for the example multiply_verylong("23345", "34565"), but you should do further tests.

How to convert char string into hex value

I have one situation where i am receiving characters serially, byte by byte in USB Tx mode.
Now I'm stuck at where I am receiving 7 and a and my objective is to create 0x7a data.
Please provide me some workaround.
void converttohex(int recsize) {
BYTE ccount = 0;
//BYTE *recptr = (BYTE*)calloc(CommOP_Rx.bDataLength, sizeof(BYTE));
*recptr = 88;
*(recptr + 1) = 16;
*(recptr + 2) = 1;
*(recptr + 3) = 224;
*(recptr + 4) = 1;
for (xp = 12, s = 5; xp < (CommOP_Rx.bDataLength - 4); xp++,s++) {
if (xp == 12) {
for (xp = 12; (*(recimage + xp)) != ','; xp++)
ccount++;
if (ccount == 1) {
xp = 12;
xq = (*(recimage + xp) - 48);
*(recptr+s) = xq;
xp++;
} else
if (ccount == 2) {
xp = 12;
xq = (*(recimage + xp) - 48) * 10;
xw = (*(recimage + (++xp)) - 48);
*(recptr + s) = xq + xw;
xp++;
} else
if (ccount == 3) {
xp = 12;
xq = (*(recimage + xp) - 48) * 100;
xw = (*(recimage + (++xp)) - 48) * 10;
xe = (*(recimage + (++xp)) - 48);
*(recptr+s) = xq + xw + xe;
xp++;
}
}
xp++;
if (((*(recimage + xp)) == 'a') || ((*(recimage + (++xp))) == 'a')) {
--xp;
if (((*(recimage + xp)) == 'a')) {
xq = (*(recimage + xp) - 48) * 10;
} else
xq = (*(recimage + xp) - 48);
xw = 'a';
*(recptr + s) = xq +xw;
}
xq = (*(recimage + xp) - 48) * 10;
xw = (*(recimage + (++xp)) - 48);
*(recptr + s) = xq + xw;
}
for (xp = 0; xp < (CommOP_Rx.bDataLength - 4); xp++) {
*(recimage + xp) = *(recptr + xp);
}
Basically here i am sampling some data, which is the array of image in hex, and storing it in my MCU array. But here i have implemented that when i will receive a two consecutive data in which which is an Integer 0-9, then i am subtracting 48 into it as all the bytes are in character encoding, upto this it is working fine, but after receiving A-F of hex, lets suppose i have received '7' & 'a' and it has to be value=0x7a , thus i need this conversion help!!
Store it in a null-terminated string and read from it.
// Assume N is a big number
char str[N] = "7A";
int ret;
sscanf(str, "%x", &ret);
printf("%d", ret); // Output: 122
To do it repetitively, let the program construct this array:
int i = 0;
while (read_data(&ch)){
// Assume ch is in "0123456789ABCDEFabcdef"
str[i++] = ch;
}
str[i] = 0;
// Now parse it
int ret;
sscanf(str, "%x", &ret);
You can store the hex digits in an array of char, null terminate this array an use sscanf() or strtol() to convert the string to a number.
Your code seems quite complicated for me to understand. Just giving a glimpse of what can be done to get hexadecimal value based on your question and not based on your code. Sorry for that.
If you process the two inputs as string then the below approach will suite.
char num1[] = "7";
char num2[] = "a";
char num[10];
strcpy (num, num1);
strcat (num, num2);
Now num has 7a as hexadecimal string
If you have the two inputs as integers then this logic will work
int num1,num2,num3;
num1=0x7;
num2=0xa;
num3=(num1<<4)|(num2);
sprintf(str,"0x%x",num3);

sprintf replacement for embedded systems [duplicate]

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.
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.
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);
}
// 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
}
}
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
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.
Its a Liitle large method, but It would work for both int and float, decimalPoint parameter is passed with zero value for Integer, Please let me know if you have smaller function than this.
void floatToStr(uint8_t *out, float x,int decimalPoint)
{
uint16_t absval = fabs(x);
uint16_t absvalcopy = absval;
int decimalcount = 0;
while(absvalcopy != 0)
{
absvalcopy /= 10;
decimalcount ++;
}
uint8_t *absbuffer = malloc(sizeof(uint8_t) * (decimalcount + decimalPoint + 1));
int absbufferindex = 0;
absvalcopy = absval;
uint8_t temp;
int i = 0;
for(i = decimalcount; i > 0; i--)
{
uint16_t frst1 = fabs((absvalcopy / pow(10.0, i-1)));
temp = (frst1 % 10) + 0x30;
*(absbuffer + absbufferindex) = temp;
absbufferindex++;
}
if(decimalPoint > 0)
{
*(absbuffer + absbufferindex) = '.';
absbufferindex ++;
//------------------- Decimal Extractor ---------------------//
for(i = 1; i < decimalPoint + 1; i++)
{
uint32_t valueFloat = (x - (float)absval)*pow(10,i);
*(absbuffer + absbufferindex) = ((valueFloat) % 10) + 0x30;
absbufferindex++;
}
}
for(i=0; i< (decimalcount + decimalPoint + 1); i++)
{
*(out + i) = *(absbuffer + i);
}
i=0;
if(decimalPoint > 0)
i = 1;
*(out + decimalcount + decimalPoint + i) = 0;
}

How to avoid sprintf when joining variables

I am working on code to get USB device details into single String, and have following code,
struct usb_bus *bus;
struct usb_device *dev;
usb_init();
usb_find_busses();
usb_find_devices();
for (bus = usb_busses; bus; bus = bus->next)
for (dev = bus->devices; dev; dev = dev->next)
{
// working outputs
printf("Trying device %s/%s\n", bus->dirname, dev->filename);
printf("Trying device2 %0x\n", dev->descriptor.idVendor);
printf("Trying device3 %0x\n", dev->descriptor.idProduct);
char deviceDetailsStr[150];
sprintf(deviceDetailsStr, "%s_%s_%0x_%0x", bus->dirname,
dev->filename,dev->descriptor.idVendor,dev->descriptor.idProduct);
... have other code here that works on "deviceDetailsStr"
}
Been reading thatt "sprintf" has performance issues, since it supports lots of transforms.
Can you please suggest what is better alternative to using "sprintf", so that all 4 variables data gets read into variable "deviceDetailsStr"
End goal is "deviceDetailsStr" char array needs to have all 4 entires as single string.
Thanks
If you want the best performance, I would say you need to write something custom. Here's an example for your specific requirements.
uint32_t printHex( char * buffer, uint32_t value ) {
uint32_t sz = value <= 0xF ? 1 :
value <= 0xFF ? 2 :
value <= 0xFFF ? 3 :
value <= 0xFFFF ? 4 :
value <= 0xFFFFF ? 5 :
value <= 0xFFFFFF ? 6 :
value <= 0xFFFFFFF ? 7 : 8;
for( uint32_t i=sz-1; i; i-- ) {
buffer[ i ] = ((value & 0xF) <= 9 ? '0' : 'a'-10 ) + (value & 0xF);
value=value>>4;
}
return sz;
}
char buffer[150];
unsigned bi=0;
for( char * ptr = bus->dirname ; *ptr; ptr++ ) buffer[bi++] = *ptr;
buffer[bi++] = '_';
for( char * ptr = bus->filename; *ptr; ptr++ ) buffer[bi++] = *ptr;
buffer[bi++] = '_';
bi += printHex( buffer + bi, dev->descriptor.idVendor );
buffer[bi++] = '_';
bi += printHex( buffer + bi, dev->descriptor.idProduct );
buffer[bi] = '\0';
Note: There are no size checks (just like sprintf). Again if you want best performance, this is a trade off.
Step 1: Determine the maximum buffer size needs.
Assume bus->dirname, dev->filename are arrays.
#define Mark_SZ ((sizeof bus->dirname - 1) + 1 + \
(sizeof dev->filename - 1) + 1 + \
((sizeof dev->descriptor.idVendor * CHAR_BIT + 3) /4) + 1 + \
((sizeof dev->descriptor.idProduct * CHAR_BIT + 3) /4) + 1)
#defined Extra (depends on: "other code here that works on "deviceDetailsStr"")
char deviceDetailsStr[Mark_SZ + Extra];
Step 2: Copy in each part
// Some untested code to give you an idea.
char *p = deviceDetailsStr;
size_t n = strlen(bus->dirname);
memcpy(p, bus->dirname, n);
p += n;
*p++ = '_';
n = strlen(dev->filename);
memcpy(p, dev->filename, n);
p += n;
*p++ = '_';
p += sprintf(p, "%x", dev->descriptor.idVendor);
*p++ = '_';
sprintf(p, "%x", dev->descriptor.idProduct);
I coded sprintf(p, "%x", dev->descriptor.idVendor) and sprintf(p, "%x", dev->descriptor.idProduct) by themselves as hoping even a modest compiler will recognize this and replace with the equivalent itoa() like function calls. Otherwise, simply code up a replacement unsigned to string.
I see no value with "0" in "%0x"

Convert float to string without sprintf()

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.
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.
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);
}
// 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
}
}
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
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.
Its a Liitle large method, but It would work for both int and float, decimalPoint parameter is passed with zero value for Integer, Please let me know if you have smaller function than this.
void floatToStr(uint8_t *out, float x,int decimalPoint)
{
uint16_t absval = fabs(x);
uint16_t absvalcopy = absval;
int decimalcount = 0;
while(absvalcopy != 0)
{
absvalcopy /= 10;
decimalcount ++;
}
uint8_t *absbuffer = malloc(sizeof(uint8_t) * (decimalcount + decimalPoint + 1));
int absbufferindex = 0;
absvalcopy = absval;
uint8_t temp;
int i = 0;
for(i = decimalcount; i > 0; i--)
{
uint16_t frst1 = fabs((absvalcopy / pow(10.0, i-1)));
temp = (frst1 % 10) + 0x30;
*(absbuffer + absbufferindex) = temp;
absbufferindex++;
}
if(decimalPoint > 0)
{
*(absbuffer + absbufferindex) = '.';
absbufferindex ++;
//------------------- Decimal Extractor ---------------------//
for(i = 1; i < decimalPoint + 1; i++)
{
uint32_t valueFloat = (x - (float)absval)*pow(10,i);
*(absbuffer + absbufferindex) = ((valueFloat) % 10) + 0x30;
absbufferindex++;
}
}
for(i=0; i< (decimalcount + decimalPoint + 1); i++)
{
*(out + i) = *(absbuffer + i);
}
i=0;
if(decimalPoint > 0)
i = 1;
*(out + decimalcount + decimalPoint + i) = 0;
}

Resources