micropython / micropython

MicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems
https://micropython.org
Other
19.21k stars 7.69k forks source link

stm port - gcc promotes floats to doubles with printf ... type vargs #110

Closed hagenkaye closed 7 years ago

hagenkaye commented 10 years ago

So, I'm implementing printf for the stm port to do printing of floats. It seems gcc will automatically promote floats to doubles when you use a function like printf with ... args

Interesting. and this isn't necessarily bad.

inside the guts of the printf function there has to be a function that takes a double and converts it to a string. It seems in my digging about for different sources that do this (it's not as easy as one might think to convert a double to the string) that modern versions of this (including musl) further take the double and convert it to a long double. I found no variations of source code that takes a float and converts it.

So, it seems that a printf with a floating point value is going to come at some cost of converting a float to something else before it can be processed anyways.

the auto promotion of gcc from floats to doubles with ... type functions saves a lot of tweaking of existing code. In fact I was able to use the standard printf function in the musl C library without modification.

pfalcon commented 10 years ago

Yes, @dpgeorge mentioned this already, thanks for opening ticket.

that modern versions of this (including musl) further take the double and convert it to a long double.

Some say "modern", some say "bloated". And apparently, musl is bloated libc, not suitable for MCU usage ;-).

So, it seems that a printf with a floating point value is going to come at some cost of converting a float to something else before it can be processed anyways.

?? Because people who have wealth of having double type do it like that, doesn't mean it has to be done like that by everyone.

In fact I was able to use the standard printf function in the musl C library without modification.

Surely, and everyone has been able to run CPython as is for years. Maybe we should just attach HDD to MCU and use it? ;-)

Let's discuss programming-art solution to a problem.

So, as modern Cs don't allow to do (void*)1.0f, apparently, we'd need to do what @dpgeorge mentioned - pass float by reference, and have custom formatter of course:

printf("%F", &o->value);
udif commented 10 years ago

float to double promotion is not handled by the printf() code. It is done automatically for all function calls that either have no prototypes defined, or have varargs (...) .

https://blogs.fau.de/wittmann/2013/11/c-default-argument-promotions/ http://stackoverflow.com/questions/6395726/how-does-printf-and-co-differentiate-beetween-float-and-double

pfalcon commented 10 years ago

That's obvious and you missed the point - it's done for all floats passed by value, so if you want floats, you should not pass them by value.

hagenkaye commented 10 years ago

Musl is far from being bloated. It's a modern c library written to be feature rich, quick and correct. Here is a quick summary, in case you haven't visited the website or looked at the code

http://www.etalabs.net/compare_libcs.html

Converting a floating point number to a string is not a trivial undertaking. The BSD library has been using the internal gdtoa function -

http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/gdtoa/gdtoa.c?rev=1.4;content-type=text%2Fplain

scroll down to the point in the code where it reads-

/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
 *
 * Inspired by "How to Print Floating-Point Numbers Accurately" by
 * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].

The musl library has a new modern algorithm, implemented in the fmt_fp function here-

http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfprintf.c

The code is cleaner and implemented very well inside stdio, which makes implementing formatted strings and stream io consistent, correct and easy. Modern compilers do the heavy lifting of supporting long doubles when the underlying hardware only has support for single precision floats.

I don't think rolling a new method to pass floats to printf functions is that great of an idea. While it may satisfy some criteria of not passing a double, the underlying code that will do the printf function will be converting it to a higher precision to accurately print floating values anyways. As well, it strays away from standards. By using a standard C library not only is the code correct and time tested, it allows the possibility of using other important libraries that require some sort of libc to work.

dpgeorge commented 10 years ago

How much extra processing overhead is there for musl to convert to a long double? 128 bits is a lot.

Does musl printf allocate heap memory when doing this, or when printing the long double? Does it need a lot of stack?

hagenkaye commented 10 years ago

These are good questions to consider. Right now I'm just going through the implementation phase - bringing the code straight in, making small modifications to keep it as similar to the original code to ensure accuracy of the port.

The actual code that performs the conversion from a floating point to string is contained within one function. So optimizing or replace the code in this particular function should be relatively easy as it should have minimal impact on the code that calls it.

There are MIT license compatible source snippets that do the floating point to string using only double instead of long doubles. For example:

http://piumarta.com/software/fcvt/fcvt-1.1/fcvt.c

I'm thinking this code could be used to replace the underlying fmt_fp algorithm. It is based on doubles rather than long doubles.

I haven't found any code sources to do this just for floats, I'm thinking using float just might not be accurate enough


As a side note, @udif you are correct. float to double promotion only happens with varargs and prototypes are not defined. If a functions is defined as-

float sinf (float x)

Then no promotion occurs. The floats are passed as values in the floating point registers.

hagenkaye commented 10 years ago

.

pfalcon commented 10 years ago

Ok, as this issue is not really resolved (in particular, answers to @dpgeorge's questions above are not given), I'm reopening this.

dpgeorge commented 10 years ago

I think this is out of date. floats are used in stmhal/ everywhere, and conversion to double is minimised. There are quite a few math functions taken from musl in a very hacky way, but it's okay for now.

dpgeorge commented 7 years ago

This can be closed because:

erhan9398 commented 5 years ago

if you need to print float value, you can use this
" printf(" float value: %.3f", static_cast(float_number)); " also you can arrange digit number after dot, for this example it is 3.