LinuxQuestions.org
Support LQ: Use code LQ3 and save $3 on Domain Registration
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software
User Name
Password
Linux - Software This forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.

Notices

Reply
 
Search this Thread
Old 08-20-2012, 04:02 PM   #1
GreyBeard
LQ Newbie
 
Registered: Oct 2003
Location: Taxachusetts, USA
Distribution: Slackware
Posts: 25

Rep: Reputation: 0
Passing a float variable to a function in C modifies caller's state.


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_cal...ventions#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???

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

========================================================================

Here is some version information:

> uname -a
Linux atomik 2.6.37.6-smp #1 SMP Sat Apr 9 14:01:14 CDT 2011 i686 Intel(R) \
Atom(TM) CPU D510 @ 1.66GHz GenuineIntel GNU/Linux

> cat /etc/slackware-version
Slackware 13.37.0

> gcc --version
gcc (GCC) 4.5.2

> ls -1 /var/log/packages/glib*
/var/log/packages/glib-1.2.10-i486-3
/var/log/packages/glib2-2.28.6-i486-1
/var/log/packages/glibc-2.13-i486-4
/var/log/packages/glibc-i18n-2.13-i486-4
/var/log/packages/glibc-profile-2.13-i486-4
/var/log/packages/glibc-solibs-2.13-i486-4
/var/log/packages/glibc-zoneinfo-2.13-noarch-4
/var/log/packages/glibc-zoneinfo-2011i_2011n-noarch-1


And here is the program followed by its output. Watch (int)floatVarA change
after successive calls to printf() just passing a float.

========================================================================

// Program to show UNDOCUMENTED side-effects IN THE CALLER'S SPACE when passing
// float variables to subroutines which should change NOTHING in the caller's
// space. See http://en.wikipedia.org/wiki/X86_cal...ventions#cdecl where
// it says, "In Linux/GCC, double/floating point values should be pushed on the
// stack via the x87 pseudo-stack." I'm not sure but that may be related.

#include <stdio.h>
#include <stdlib.h>

int
main ( int argc, char *argv[])
{
int intVarA = 37;
int intVarB = 42;

float floatVarB = 42.0;
float floatVarA = 37.0;

// Show where things are and their sizes.

printf("\nintVarA is at %p of size %d\n", &intVarA, sizeof( intVarA));
printf("intVarB is at %p of size %d\n", &intVarB, sizeof( intVarB));

printf("\nfloatVarA is at %p of size %d\n", &floatVarA, sizeof( floatVarA));
printf("floatVarB is at %p of size %d\n", &floatVarB, sizeof( floatVarB));

// Do all the assignments.

intVarA = 1234;
intVarB = 5678;

floatVarA = 1234.0;
floatVarB = 5678.0;

// From here on out nothing in main's space should change and for
// calls with int types it does not ...

printf( "\n(int)intVarA = %d\n", (int)intVarA); // Prints 1234
printf( "intVarA = %d\n", intVarA);
printf( "(int)intVarA = %d\n", (int)intVarA); // Prints 1234
printf( "intVarB = %d\n", intVarB);
printf( "(int)intVarA = %d\n", (int)intVarA); // Prints 1234
printf( "intVarA = %d\n", intVarA);

// ... but something IS changing in MAIN's space with float variables. BAD!

printf( "\n(int)floatVarA = %f\n", (int)floatVarA); // Prints 0.0
printf( "floatVarA = %f\n", floatVarA);
printf( "(int)floatVarA = %f\n", (int)floatVarA); // Prints 1234.0
printf( "floatVarB = %f\n", floatVarB);
printf( "(int)floatVarA = %f\n", (int)floatVarA); // Prints 5678.0
printf( "floatVarA = %f\n", floatVarA);

return 0;
}

========================================================================

> cc float_passing_insanity.c && ./a.out

intVarA is at 0xbf80155c of size 4
intVarB is at 0xbf801558 of size 4

floatVarA is at 0xbf801550 of size 4
floatVarB is at 0xbf801554 of size 4

(int)intVarA = 1234
intVarA = 1234
(int)intVarA = 1234
intVarB = 5678
(int)intVarA = 1234
intVarA = 1234

(int)floatVarA = 0.000000
floatVarA = 1234.000000
(int)floatVarA = 1234.000000
floatVarB = 5678.000000
(int)floatVarA = 5678.000000
floatVarA = 1234.000000

========================================================================
 
Old 08-20-2012, 04:35 PM   #2
johnsfine
Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,109

Rep: Reputation: 1114Reputation: 1114Reputation: 1114Reputation: 1114Reputation: 1114Reputation: 1114Reputation: 1114Reputation: 1114Reputation: 1114
Edit: After reading your question more carefully, I see I focused too much on the question you asked rather than your original problem that launched you into this misguided experiment.

"%f" in printf is for doubles, not floats. When you pass a float, printf receives a double (see more info below).

I don't know exactly what the original code (that you didn't post) did wrong, but the symptoms imply you managed to suppress the actual conversion of float to double and instead reinterpret a float and the following unrelated four bytes as a double.

Quote:
Originally Posted by GreyBeard View Post
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,
I understand exactly why you get exactly that effect. But it is only effecting the symptoms of a bug in your code. The symptoms of incorrect code are not supposed to be stable or predictable.

Quote:
Undocumented
side effects in the caller's space strike me as being very bad.
Under these circumstances, I disagree. There is nothing wrong with side effects to an area of memory whose contents only influences the behavior of incorrect subsequent code. The behavior of correct subsequent code would not be affected.

Specifically, your code is depending on the contents of an undefined portion of the stack by passing data that is too small into printf (in 32 bit x86). In other architectures, this same passing of wrong info to printf would have different symptoms.

Quote:
It is the passing of a float variable which
is at fault.
Nonsense. Passing a float is fine. Calling a function typically modified the contents of the undefined portion of the stack (as viewed after the function completes). In this case the value of the float is left in an undefined portion of the stack.

Quote:
There is some weirdness
going on which I do not understand concerning how floats are passed.
Nothing weird here at all (you should see how floats are passed in win64). In x86 32 bit, parameters are simply passed on the stack.

Quote:
I tried to pass that buffer to printf()
using the proper formatting string for the number's type
How is the format string proper when you have cast the number to a different type.

Quote:
and a cast on the
buffer to get the right length
Maybe that is the key to your confusion. You are casting the value to a different type. How do you see that as casting a buffer to a different length?

Quote:
After all a float is
the same size as an int
Look up "default argument promotion", which applies because this is part of a variable length argument list. The float is passed as a double.

You should know that "%f" is the format for double, not for float.

Quote:
WHY is a 32-bit float passed to a function in a manner different from a
32-bit int???
That is generally true and the details vary a lot by architecture and goes beyond the fact that floats may be promoted to doubles. So there may be more to making your code usable (especially in any portable way) than simply realizing that %f is for doubles.

Last edited by johnsfine; 08-20-2012 at 05:03 PM.
 
  


Reply

Tags
arguments, float, functions, programming


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Passing variable arguments to ctypes function of C++ prasanthhs Programming 1 08-09-2012 08:30 PM
passing array and variable to function in bash script ajaypitroda Programming 2 07-07-2009 11:10 PM
calling a function and passing a variable the correct way ForYouAndI.com Programming 2 06-07-2007 09:44 PM
Perl question: Passing variable into function rjcrews Programming 0 10-06-2006 09:24 AM
Bash Script Passing variable to Function nutthick Programming 2 02-02-2005 05:15 AM


All times are GMT -5. The time now is 01:33 PM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration