C Puzzler – Give Me A Float

Time for another Puzzler! This time, let’s do a C Puzzler :) For those that don’t know what a Puzzler is, it is essentially a short problem used to demonstrate special cases or “features” of a particular language. Here is the format:

  1. Code – I introduce the code
  2. Question – I pose a multiple-choice question and you guess what the outcome is…think hard!
  3. Walkthrough – I walk through a reasonable explanation
  4. Answer – I tell you the real outcome (it might surprise you), and explain why
  5. Moral – How can we avoid making mistakes like this in our own code

Now that we all know what a Puzzler is, here is a simple C Puzzler:

Code:

// c-puzzler-give-me-a-float.c
#include <stdio.h>
 
int main()
{
	int x = 0.5 + giveMeAFloat();
 
	return x;
}
// c-puzzler-give-me-a-float-please.c
float giveMeAFloat()
{
	return 0.5;
}

Question:

What does this print?

  1. 0
  2. 1
  3. 1056964608
  4. it varies

Walkthrough:

There is a local variable, x, and it is declared as an int. It is being initialized to the sum of 0.5 and the value returned by the function giveMeAFloat, which is implemented in another source file. The function giveMeAFloat returns the float value 0.5, and so the sum of these two values is the float value 1.0. The variable x is an int, and so it will cast the value from 1.0 to 1. So, my answer is b, main returns the int value 1.

 

*SPOILER ALERT – ANSWER BELOW*


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Answer:

The answer is actually d – it varies! But, in practice, you will likely get c – 1056964608. How does this happen? Well, the astute observer will notice that in the source file containing our main function, there is no function prototype declaring the function giveMeAFloat. Because of this, and the fact that giveMeAFloat is implemented in another source file, then giveMeAFloat is *assumed* to return an int and not a float! If the method were declared and defined within the same source file, the compiler would detect this error and not complete the compilation. However, due to separate compilation, this cast goes unnoticed. And so, because of this ambiguity, depending on the compiler and the platform, how this value is treated and stored will vary from machine to machine! To better understand this, let’s look at the practical case where the answer is 1056964608.

  • This assumes that the program is being executed on a typical machine setup, with an x86 architecture running a current version of gcc (v4.x+)
    When the program is executed, within main, an int variable, x, is declared and initialized with a simple assignment expression that invokes giveMeAFloat. When giveMeAFloat returns, it stores the value 0.5 into the return register, eax. Since we are on a little-endian machine, this value is 0x3F000000. However, since the compiler is expecting to read an integer value, it interprets 0x3F000000 as an int and not a float, effectively loading the value 1056964608 (this is the decimal interpretation of the floating point hex representation of 0x3F000000…yikes!). Back in main, the value 0.5 gets added to this, resulting in the float value 1056964608.5. This subsequently gets assigned to the variable x. Since x is an int, this value gets cast and therefore truncated to 1056964608. And so main returns 1056964608.
     

As you can see, there are many factors that can affect the return value. In particular, what value the compiler stores into the return register (if anything), how that value is stored internally (endian-ness of the host machine), and how that value is subsequently read from the register (interpreted as int, or float, or other) all play a part. Due to all of these factors, the return value from main is effectively unpredictable, and should be treated as such (regardless of what the practical case would yield)!

Moral:

Given an arbitrary name that has not yet been previously declared, if the name is followed by a left parenthesis, it is defined to be a function and it is assumed to return an int. To prevent this, explicitly declare the function, either as a function prototype at the top of the source file, like so…

#include <stdio.h>
 
float giveMeAFloat();
 
int main()
{
	int x = 0.5 + giveMeAFloat();
 
	return x;
}

…or within the calling routine, like so…

#include <stdio.h>
 
int main()
{
	float giveMeAFloat();
	int x = 0.5 + giveMeAFloat();
 
	return x;
}

Now, there is no confusion, to yourself or the compiler, about the return-type of the function giveMeAFloat().

That’s been another C Puzzler! Hope you enjoyed! Happy coding!

 

 
Charles

c-puzzler-give-me-a-float.zip (source code)

Update: This post was corrected based on some feedback in the comments by Mihai. Thanks for the feedback, Mihai!

7 Responses to C Puzzler – Give Me A Float

  1. Pingback: C Puzzler – Give Me A Float

  2. Pingback: Tweets that mention C Puzzler – Give Me A Float « Charles @ Adobe -- Topsy.com

  3. Mihai says:

    Actually, the answer is wrong, and in reality it is also platform & compiler dependent.
    1. giveMeAFloat returns a float (no reason not to)
    2. The result is (usually) returned in eax
    But that is a compiler specific convention. Some compilers might choose to
    use different return conventions for float and double vs. int
    3. The calling module assumes (indeed) that the “stuff” in eax is an int, so not a zero
    (and if float results are not returned in eax, will get junk)

    So the bytes in eax are interpreted as int, added with a 0.5 float (which does not affect the result), and the end result is (usually) a big integer (the exact value depends on the internal representation of a float on that platform).

    It is enough to un-comment the printf from the posted sample code :-)

    • Charles Bihis says:

      Hi Mihai,

      You’re absolutely right! The point of this post was to illustrate that in the ANSII C specification, the default behavior of such a case would be to assume a return-type of int on any yet undeclared functions. This is still the case, but I hadn’t taken into account variations in compiler implementation or platform floating-point and integer representation. My answer, incorrectly assumed a particular compiler implementation and platform…oops! I mainly tested with gcc v3.4.4, which gave me 0, but with gcc 4.1.2, I get the integer representation of the floating point number 0.5 on my machine. So yes, the answer is indeed compiler- and platform-specific. I’ve updated the post accordingly! Thanks for the correction!

  4. Mihai says:

    Looks like gcc (by default) returns the result of float function in the floating point stack, while the caller expects an int and examines eax.
    So the result in this case is almost random: whatever happened to be in eax at the time.

    • Charles Bihis says:

      Really? I’ve been able to replicate with predictable results (1056964608) on all of the x86 machines I’ve tried with gcc v4.x. I have had quite varying results, though, when using older versions of gcc, as well as other architectures.

  5. Pingback: มาเล่น ActionScript Puzzler กันเถอะ