I am running a bare metal embedded system with an ARM Cortex-M3 (STM32F205). When I try to use snprintf()
with float numbers, e.g.:
float f;
f = 1.23;
snprintf(s, 20, "%5.2f", f);
I get garbage into s
. The format seems to be honored, i.e. the garbage is a well-formed string with digits, decimal point, and two trailing digits. However, if I repeat the snprintf
, the string may change between two calls.
Floating point mathematics seems to work otherwise, and snprintf
works with integers, e.g.:
snprintf(s, 20, "%10d", 1234567);
I use the newlib-nano
implementation with the -u _printf_float
linker switch. The compiler is arm-none-eabi-gcc
.
I do have a strong suspicion of memory allocation problems, as integers are printed without any hiccups, but floats act as if they got corrupted in the process. The printf
family functions call malloc
with floats, not with integers.
The only piece of code not belonging to newlib
I am using in this context is my _sbrk()
, which is required by malloc
.
caddr_t _sbrk(int incr)
{
extern char _Heap_Begin; // Defined by the linker.
extern char _Heap_Limit; // Defined by the linker.
static char* current_heap_end;
char* current_block_address;
// first allocation
if (current_heap_end == 0)
current_heap_end = &_Heap_Begin;
current_block_address = current_heap_end;
// increment and align to 4-octet border
incr = (incr + 3) & (~3);
current_heap_end += incr;
// Overflow?
if (current_heap_end > &_Heap_Limit)
{
errno = ENOMEM;
current_heap_end = current_block_address;
return (caddr_t) - 1;
}
return (caddr_t)current_block_address;
}
As far as I have been able to track, this should work. It seems that no-one ever calls it with negative increments, but I guess that is due to the design of the newlib malloc
. The only slightly odd thing is that the first call to _sbrk
has a zero increment. (But this may be just malloc
's curiosity about the starting address of the heap.)
The stack should not collide with the heap, as there is around 60 KiB RAM for the two. The linker script may be insane, but at least the heap and stack addresses seem to be correct.
double
s, notfloat
s. No idea if it matters, you can't pass afloat
tosnprintf()
anyway. – Anglimsnprintf()
isint snprintf(char *restrict s, size_t n, const char *restrict format, ...);
You're not calling the function according to the prototype. Did you#include <stdio.h>
and compiled with all warnings enabled? – Winnipegosissnprintf()
and notsprintf()
? – Beadrubyprintf
is a variadic function automatically promoting floats to doubles. (At leastgcc
does not complain about the formats with its pedantic settings.) With my original example (literal constant1.23
) the argument is a double anyway, but in the revised example it is a single-precision float. I am not sure aboutnewlib nano
's innards, I would guess it rather keeps floats as floats because doubles are quite laborious in embedded systems (but this is just a guess). – Duyneprintf
in an embedded system, I have enough performance to count the characters to avoid any stupid accidents. IMHO,sprintf
should not exist; it is just plain dangerous. – Duynesnprintf(s, "%.2f", 1.23)
and I was suspecting that instead of not getting/ignoring compiler warnings, there was a code cut/paste error. – Beadruby-pedantic
. I have been forced to drop the-Werror
, as I use the STM32 HAL, which unfortunately triggers an awful number of padding and conversion warnings. – Duyne%f
normally involves convertingfloat
todouble
; the EABI mandates 8-byte alignment fordouble
; your_sbrk()
only enforces 4-byte alignment on what it hands out. How much that matters probably depends on the guts ofprintf()
andmalloc()
, but it should be straightforward to experiment with. – Whipperin-u _printf_float
? I'm trying to compile for a 16kB part (STM32F030F4P6) and the binary seems to be too large (about 20 kB). – Saber