Re: Calling a function with a float argument modifies caller's state.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 20/08/2012 22:21, Jeff B wrote:

Hi,

I've run into a problem which appears to be doing an undocumented change
of state in the caller's space when passing float variables to a function,
printf(), which should change nothing in the caller's space. Undocumented
side effects in the caller's space strike me as being very bad.

printf() here is not at fault.  It is the passing of a float variable which
is at fault.  I am compiling with GCC under Linux.  There is some weirdness
going on which I do not understand concerning how floats are passed .  See

     http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl

I originally ran into this problem when I was putting binary values of
various types into a buffer.  When I tried to pass that buffer to printf()
using the proper formatting string for the number's type and a cast on the
buffer to get the right length (but not "(float)") all the printf() formats
worked except for "%f", the output of which was "-nan", ie Not A Number.

I then started asking myself why this did not work.  After all a float is
the same size as an int so why should there be a problem of "%d" vs. "%f"
printf() formats?  Well, I found the aforementioned link which told me this
is probably some weirdness in GCC "C" specific to float function arguments.
Aside from the asymmetry of passing various 32-bit types differently I ran
into something else when I started playing around with it, that being that
a function call using floats which modifies state visible to the caller.

The program below shows there is no problem when passing ints to printf()
but the same code using floats alters something -- has a side-effect which
is visible to the caller.

WHY is a 32-bit float passed to a function in a manner different from a
32-bit int???


A 32-bit float and a 32-bit int are completely different things. The fact that the have a common size is just coincidence. The compiler can treat them totally differently - on many targets (including, I believe, the x86 - depending on the compiler flags) they will be passed around in different registers. If it suits the cpu in question, the compiler can happily convert the 32-bit float to a 64-bit (or even higher) resolution before passing it around. You certainly cannot rely on being able to convert randomly between ints and floats while keeping the same exact values.

When passing data into printf(), the compiler can take the actual format string into account. When you write "printf("%f", x)", you are /not/ saying "pass a 32-bit value x, treat it as representing a float, and print it". The compiler may /choose/ to treat it that way. But it can also validly interpret it as "treat the value x as a float, and print it out". When you use C properly, according to the specifications of printf, these do the same thing - but one way might do it with smaller or faster code, or with better static error checking. When you break C rules by your mixed casting, they can mean different things.

I also think that it is a really bad idea to have undocumented side effects
like that.  Or did I already say that?


I think most people will agree that undocumented side effects are a bad idea. But your program does not demonstrate undocumented side effects.

Your program demonstrates that when you write code that has undefined behaviour, it does not work as you expect. That should not be a big surprise.

You also don't demonstrate that the calls to printf are modifying the caller's state. My immediate interpretation of your result is that the compiler is doing alias analysis - it knows what can and cannot be legally done in conversions between unrelated types (int and float), and it uses this knowledge to generate code that is more optimal under the assumption that the programmer is following the rules of C. When you break those rules, you break that assumption - lie to your compiler and you will get bad code as a result.


Try compiling your code with -Wall, which will help point out your errors.



[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux