Printing a float in C while avoiding variadic parameter promotion to double
Asked Answered
C

4

13

How can I print (that is, to stdout) a float in C without having it be promoted to a double when passed to printf?

The issue here is that variadic functions in C promote all float parameter to double, which incurs two unnecessary conversions. For example, if you turn on -Wdouble-promotion in GCC and compile

float f = 0.f;
printf("%f", f);

you will get

warning: implicit conversion from 'float' to 'double' when passing argument to function

I have relatively little processing power to play with (a 72MHz ARM Cortex-M3), and I am definitely bottlenecking on ASCII output of floating point data. As the architecture lacks a hardware FPU to begin with, having to convert between single and double precision does not help matters.

Is there a way to print a float more efficiently in straight C?

Conradconrade answered 2/4, 2011 at 8:15 Comment(1)
If anyone comes across this, I ended up just base64'ing my data, and decoded/formatted/pretty-printed everything on a host machine.I also considered Boost::Karma, but that would have required C++ and there was a good chance that it wouldn't save me any code size.Conradconrade
F
9

Avoiding the promotion will not save you anything, since the internal double (or more likely long double) arithmetic printf will perform is going to consume at least 1000x as much time. Accurately printing floating point values is not easy.

If you don't care about accuracy though, and just need to print approximate values quickly, you can roll your own loop to do the printing. As long as your values aren't too large to fit in an integer type, first convert and print the non-fractional part as an integer, then subtract that off and loop multiplying by 10 and taking off the integer part to print the fractional part one digit at a time (buffer it in a string for better performance).

Or you could just do something like:

printf("%d.%.6d", (int)x, (int)((x-(int)x)*1000000));
Frustrated answered 2/4, 2011 at 14:17 Comment(1)
I realized that most of the floats I needed to print were in the range of [0, 1], so I was able to scale then round those to integers for printing. This improved performance by quite a lot.Conradconrade
A
6

Unfortunately, printf does not have support for handing plain float:s.

This mean that you would have to write your own print function. If you don't need the full expressive power of printf, you could easily convert your floating-point value to an integral part and a part representing a number of decimals, and print out both using integers.

If, on the other hand, you simply would like to get rid of the warning, you could explicitly cast the float to a double.

Amylene answered 2/4, 2011 at 8:21 Comment(0)
C
2

I think that doesnt matter - printf is already such a timeconsuming nasty thing, that those conversion should not matter. The time converting float to double should be far less than converting any number to ascii (you should/could profile your code to get there a definitve answer). The only remaining solution would be to write an own custom output routine which converts float->ascii and then uses puts (or similar).

Confer answered 2/4, 2011 at 8:29 Comment(2)
I don't agree. The most expensive part of floting-point to ascii conversions is the divisions by 10. And those do get faster if working on float and not on double.Seeley
@edgar: There are of course floating points ops involved that costs more as double than as floats. But the div by 10 implementation depends on the implementation of the lib. You could also just work on the mantissa bits with integer operations for getting the digits. That depends heavily on the used libc (which is for embedded usually not a full big implementation). Thats why I said that you need profile for a definitve answer. And the only way to avoid is writing your own function.Confer
S
1
  • First approach: Use ftoa instead of printf. Profile.

  • For increased output flexibility, I would go into the source code of your compiler's stdlib, perhaps some derivative of gcc anyway, locate the printf implementation and copy over the relevant code for double -> ascii conversion. Rewrite it to float -> ascii.

  • Next, manually change one or two porminent call-sites to your new (non-variadic) version and profile it.

  • If it solves your problem, you could think of rewriting your own printf, based on the version from stdlib, whereby instead of float you pass float*. That should get rid of the automatic promotion.

Seeley answered 2/4, 2011 at 9:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.