sprintf concerns (floating point)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

sprintf concerns (floating point)

4,706 Views
rhb3
Contributor II
In regards to this code below:
 
Code:
    volatile int cnt;    char flt_buf[15] = {0};    /* Load the float into a string */    __DI();    if (dp == 1U) {        /* Showing tenths */        cnt = sprintf(&flt_buf[0], "%-5.1f", val);        /* Override term! */        term = 0U;    }    else {        /* No tenths */        cnt = sprintf(&flt_buf[0], "%-5.0f", val);    }    __EI();

 
1) Is it necessary to protect ansilibb (banked) library calls from interrupts as shown...I seem to have (odd COP resets otherwise)?
2) Is ansilibb reentrant (if isr code calls a ansilibb function [could be same function] will this fail)?
3) I'm a bit concerned that the width specifier is a "minimum" value and this could be overrun...what are some good ideas to protect against this other that oversizing the buffer?
 
Thanks!
 
Labels (1)
5 Replies

1,391 Views
CompilerGuru
NXP Employee
NXP Employee
I have a couple of comments.
First, as already suggested, I would not use sprintf in a interrupt handler. It just takes too long.

The default sprintf is not reentrant. But if you really need it to be, then you can change it, there is a switch in libdef.h for this.
To use a reentrant sprintf:
- add lib\hc12c\src\printf.c to your project (before the ansi library in the link tab)
- add "-DLIBDEF_REENTRANT_PRINTF" to your compiler options
Recompile, this should give you a warning that the printf is taken out of your local object file and not out of the ansi lib.

The warning can be ignored (or switched off) (the only way to get rid of the waring otherwise is to rebuild the ansi lib).

About your concern of the buffer overflow, I have to agree. The printf %f format is generating a float number on the format "a.b", e.g. 1.2.
As the integral part is always printed, a float number of 1.0E100 will be writing more than 100 bytes, clearly overflowing your buffer.

Possibilities:
- use %g instead of %f.
- do not use sprintf, but some other float output formatting function.
- use the (non ANSI) vsprintf function. See the sprintf implementation in printf.c how it does, then it is simple to detect a possible buffer overflow.
- do not print huge float numbers (also not very small, negative ones...)

Daniel
0 Kudos

1,391 Views
rhb3
Contributor II

Thanks for the ideas.  I'm considering range checking and staying with the %f format...something like:

void dig4_float_to_7seg(float val, unsigned char dp, unsigned char term,  unsigned char *buf)

Code:

    volatile int len;    volatile int cnt;    char flt_buf[15] = {0};    /* Limit the float size.  This will help protect the buffer for %f format */    if ((val < -9999.5F) || (val > 9999.5F)) {        dig4_float_to_7seg_err(&buf[0]);        return;    }    /* No deeper than 1/100s resolution */    if ((val < 0.0F) && (val > -0.01F)) {        dig4_float_to_7seg_err(&buf[0]);        return;    }    if ((val < 0.01F) && (val > 0.0F)) {        dig4_float_to_7seg_err(&buf[0]);        return;    }    /* Load the float into a string.  For small numbers (near zero), %f will     * get the first significant digit for both .1 and .0 formats. */    __DI();    /* Showing tenths */    if (dp == 1U) {        cnt = sprintf(&flt_buf[0], "%-10.1f", val);        /* Override term! */        term = 0U;    }    /* No tenths */    else {        /*lint -e{559} */        cnt = sprintf(&flt_buf[0], "%-10.0f", val);    }    __EI();


 
I experimented with the %g using significnat digit counts; however, I'm trying to write the "driver" generic for ints and floats for a 4-segment display with whole number and 1/10 display options.  I failed to find a way to strip of the tenths (like %-10.0f does) for %g as it is based on significant digits.  The fact that I currently am leting sprintf handle negative numbers as well does complicate the range checking a bit as I have to allow zero AND watch out for small numbers and I guess I could pass a variable to indiate the sign (TBD). 

I analyzed printf.c but I was hoping you could elaborate on the use of vsprintf and ensuring that the buffer does not overrun...

I envision soemthing like:Code:

char buffer[15];int vspf(char *fmt, ...){   va_list argptr;   int cnt;   va_start(argptr, fmt);   cnt = vsprintf(buffer, fmt, argptr);   va_end(argptr);   return(cnt);}int main(void){   float fnumber = 90.0F;   vspf("%-10.1f", fnumber);   printf("%s\n", buffer);   return 0;}


 
Exactly how do I ensure buffer is not overrun?  Also, just to clarify...based on your earlier comments there is no reason to protect sprintf with __DI()/__EI().  I never did use it in the isr I just wanted to understand its capabilities.

Thanks again!

0 Kudos

1,391 Views
CompilerGuru
NXP Employee
NXP Employee
You don`t need to disable interrupts if you do not call ?printf from within an interrupt handler (and if you dont have a preemptative OS which runs multiple tasks using sprintf or ...)

I did implement in the attachment a C99 like snprintf, it's almost the same as sprintf, but it takes as additional argument the size of the destination buffer.
You can directly use it, or you can extract the part you need.
When I look at the details of your problem, I wonder if you should use sprintf with a %f at all.
Basically you only have a very limited float range (-10000..+10000) and you only one one decimal digit after the dot.
To me, this sounds pretty easy to implement with integer (long) arithmetic only. So something like this (pseudocode, never run, never compiled :-):

Bool neg= false;
if (x < 0.0f) { neg= true; x= -x; }
if (x > 10000.0f) { tooBig(); return; }
unsigned long val= (unsigned long)(x * 10.0f + 0.5f);
char buf[30]; // much more than needed :smileyhappy:
int pos=0;
if (neg) {
buf[0]= '-';
pos++;
}
pos += sprintf(buf+pos, "%lu", val / 10);
buf[pos]= '.';
pos++;
if (oneDigMore) {
buf[pos]= (char)((val % 10) + '0');
pos++;
}
buf[pos]= 0;

Well, ok, this also needs some lines, but at least it is straight forward.

Bye

Daniel
0 Kudos

1,391 Views
CompilerGuru
NXP Employee
NXP Employee
Somehow the attachment did not get added, hope this works this time.
0 Kudos

1,391 Views
alex_spotw
Contributor III
Hi:

To avoid problems, I would suggest not to use sprintf calls in your Interrupt Service Routine code. Make the ISR as short as possible.

Regards,

Alex
0 Kudos