4

I want to convert a float (e.g. f=1.234) to a char-array (e.g. s="1.234"). This is quite easy with snprintf() but for some size and performance-reasons I can't use it (it is an embedded platform where snprintf() is too slow because it uses doubles internally).

So: how can I easily convert a float to a char-array (positive and negative floats, no exponential representation, maximum three digits after the dot)?

Thanks!

PS: to clarify this: the platform comes with a NEON FPU which can do 32 bit float operations in hardware but is slow with 64 bit doubles. The C-library for this platform unfortunately does not have a specific NEON/float variant of snprintf, so I need a replacement. Beside of that the complete snprintf/printf-stuff increases code size too much

Elmi
  • 5,899
  • 15
  • 72
  • 143
  • 2
    There's no *easily* as the only alternative would be to code the conversion yourself. This would either be very inperformant (using arithmetic operations) or very complicated and require you to *know* the representation of floating point numbers on your platform. –  May 08 '18 at 07:49
  • 1
    *" how can I **easily** convert a float to a char-array"* Use library someone else wrote. But if you are on low-end embedded and float performance is an issue, you might want to consider using integers instead. – user694733 May 08 '18 at 07:50
  • 2
    Use [fixed point math](https://en.wikipedia.org/wiki/Fixed-point_arithmetic). For example, the number `1.234` is stored as `1234`. – user3386109 May 08 '18 at 07:51
  • 2
    multiply by 1000, truncate to int, print with leading zeroes for at least field width of 4, put the decimal point 3 digits from the end. – Antti Haapala -- Слава Україні May 08 '18 at 07:52
  • @AnttiHaapala I doubt this will be faster than using `snprintf()` directly –  May 08 '18 at 07:53
  • 1
    or print `e-3` after the integer number counting thousandths :P – Antti Haapala -- Слава Україні May 08 '18 at 07:53
  • 1
    @FelixPalmen the OP said that the (emulated?) double math is slow. – Antti Haapala -- Слава Україні May 08 '18 at 07:54
  • @AnttiHaapala yes, but then better don't use floats at all, like another commenter already mentioned. –  May 08 '18 at 07:56
  • 1
    @FelixPalmen The OP also mentioned size problems, the code for `snprintf` is probably pretty big as it needs to cover all format codes whereas a multiplication by 1000, truncation and a few string manipulations are certainly shorter and faster. – Jabberwocky May 08 '18 at 07:56
  • It might be possible to use a double-dabble approach on the mantissa and then calculate the correct placement of the decimal point from the exponent ... but this **will** be complicated and probably not very efficient. –  May 08 '18 at 07:57
  • @MichaelWalz sure it will be smaller, but I doubt it will be faster ;) anyways ... not using floating point at all is often a good appproach for embedded platforms. –  May 08 '18 at 07:58
  • Btw, if you switch to fix-point math, you can leave out the `*printf()` functions and do the conversions yourself using a double-dabble implementation. this might be even better coded in assembler, if, depending on the target platform, you can e.g. make good use of processor status flags. –  May 08 '18 at 08:05
  • So to shed some light on this: the platform comes with a NEON FPU which supports 32 bit floats natively - and the application I use requires the float representation (it is a protocol thingy which depends on a connected device that wants to receive these values) – Elmi May 08 '18 at 08:05
  • 1
    @Elmi please [edit] that info into the question - comments are to be considered *ephemeral*. – Antti Haapala -- Слава Україні May 08 '18 at 08:18
  • 1
    Consider perhaps the `ftoa()` implementation at http://www.ars-informatica.ca/eclectic/ftoa-convert-a-floating-point-number-to-a-character-array-on-the-arduino/ – Clifford May 08 '18 at 08:50
  • 1
    @Elmi Tip: whatever you do, it is easy enough to run a routine that tests _every_ `float` comparing your `simpler_faster(s,x)` against `sprintf(s, "%.3f", f);` and then rate the differences. There are only 2^32 different `float`. – chux - Reinstate Monica May 08 '18 at 13:30

2 Answers2

2

For many microcontrollers a simplified printf function without float/double support is available. For instance many platforms have newlib nano and texas instruments provides ustdlib.c.

With one of those non-float printf functions you could split up the printing to something using only integers like

float a = -1.2339f;
float b = a + ((a > 0) ? 0.0005f : -0.0005f);
int c = b;
int d = (int)(b * 1000) % 1000;
if (d < 0) d = -d;
printf("%d.%03d\n", c, d);

which outputs

-1.234

Do watch out for overflows of the integer on 8 and 16 bit platforms.

-edit- Furthermore, as by the comments, rounding corner cases will provide different answers than printfs implementation.

Lanting
  • 3,060
  • 12
  • 28
  • Careful with the negative numbers: You don't want to end up with `-1.-234`. – user694733 May 08 '18 at 08:00
  • You need to be careful with `(int)(a * 1000)` since `a` might actually be `1.23399999`. – user3386109 May 08 '18 at 08:00
  • 1
    This variant is nice but still makes use of the huge printf() - which contaisn functionality for a lot more conversions than only %d which is used here – Elmi May 08 '18 at 08:02
  • @Elmi look at this [SO article](https://stackoverflow.com/questions/6920554/source-code-in-embedded-c-for-unsigned-integer-to-string). It explains how you can transform an int into a string, then you can do it without `snprintf`. – Jabberwocky May 08 '18 at 08:41
  • 1
    This will fails when `|a/1000| > INT_MAX`. Using `long long` would help to reduce the problem range. It also rounds incorrectly often for values near `x.xxx5`. – chux - Reinstate Monica May 08 '18 at 13:08
  • 3
    This answer neglects to consider the effects of rounding in floating-point calculations. For example, if `a` is 3.0004999637603759765625, it prints “3.001” when the correct result is “3.000”. – Eric Postpischil May 08 '18 at 13:36
  • 1
    `float b = a + ((a > 0) ? 0.0005 : -0.0005);` brings in the possibility of `double` math. Recommend using `float` constants rather than `double` ones: `float b = a + ((a > 0) ? 0.0005f : -0.0005f);` – chux - Reinstate Monica May 08 '18 at 16:22
0

You might check to see if your stdlib provide strfromf, the low-level routine that converts a float into a string that is normally used by printf and friends. If available, this might be lighter-weight than including the entire stdio lib (and indeed, that is the reason it is included in the 60559 C extension standard).

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226