Alfie
Alfie

Reputation: 2013

Why does this function print 0 when I pass in a nonzero value as its argument?

The code here:

#include <stdio.h>

int main(void) {
    test(7.4, 4);
    return 0;
}

void test(float height, float radius){
    printf("%f", height);    
}

Will print:

0.000000

Why is this? Why will it not just print 7.4?

Upvotes: 4

Views: 240

Answers (4)

user6359267
user6359267

Reputation:

I have compiled your program in GGC compiler, I got the following error

 float.c:11:6: warning: conflicting types for ‘test’ [enabled by default]
 void test(float height, float radius)
      ^
float.c:7:5: note: previous implicit declaration of ‘test’ was here
     test(7.4, 4);
     ^

Then I added prototype of test() function befor main() function like

void test(float, float);

Then Compiled again, I got correct output.So, add prototype of test() function.

Upvotes: 0

Marco Guerri
Marco Guerri

Reputation: 942

Under Linux with gcc this is what happens: the compiler is passing 7.4 as double, due to the fact that test prototype is defined only after main.

0000000000400506 <main>:
  400506:       55                      push   %rbp
  400507:       48 89 e5                mov    %rsp,%rbp
  40050a:       48 83 ec 10             sub    $0x10,%rsp
  40050e:       48 b8 9a 99 99 99 99    movabs $0x401d99999999999a,%rax
  400515:       99 1d 40 
  400518:       bf 04 00 00 00          mov    $0x4,%edi
  40051d:       48 89 45 f8             mov    %rax,-0x8(%rbp)
  400521:       f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
  400526:       b8 01 00 00 00          mov    $0x1,%eax
  40052b:       e8 07 00 00 00          callq  400537 <test>
  400530:       b8 00 00 00 00          mov    $0x0,%eax
  400535:       c9                      leaveq 
  400536:       c3                      retq  

That 0x401d99999999999a is your 64 bits double, i.e. 7.4. The test function is interpreting that value as a single precision number (float, as specified in the prototype), passing it to printf using cvtss2sd (float to double conversion) via xmm0:

0000000000400537 <test>:
  400537:       55                      push   %rbp
  400538:       48 89 e5                mov    %rsp,%rbp
  40053b:       48 83 ec 10             sub    $0x10,%rsp
  40053f:       f3 0f 11 45 fc          movss  %xmm0,-0x4(%rbp)
  400544:       f3 0f 11 4d f8          movss  %xmm1,-0x8(%rbp)
  400549:       f3 0f 5a 45 fc          cvtss2sd -0x4(%rbp),%xmm0
  40054e:       bf e4 05 40 00          mov    $0x4005e4,%edi
  400553:       b8 01 00 00 00          mov    $0x1,%eax
  400558:       e8 83 fe ff ff          callq  4003e0 <printf@plt>
  40055d:       c9                      leaveq 
  40055e:       c3                      retq   
  40055f:       90                      nop

This causes only a fraction of your initial 64 bits value to be converted to the actual double printed to stdout, i.e. 0x9999999a, yeilding that close to 0 result.

Upvotes: 2

Anonymouse
Anonymouse

Reputation: 945

Turn compiler warning up - and fix what it warns against. Use a function prototype for test() function.

Upvotes: 1

templatetypedef
templatetypedef

Reputation: 372982

In the program you wrote, you've called the test function without first prototyping it. Modern compilers will often reject this, but on older compilers - or compilers providing support for old C code - the program will implicitly try to deduce the argument types. You've provided 7.4 and 4 as arguments, meaning that the compiler expects that you're going to be passing in a double and an int, respectively, since 7.4 is a double literal, so it generates code to pass in the first argument as a double and the second as an int.

Later, when you actually define test, you specify that the arguments are floats, which doesn't match the earlier code. As a result, that function tries to read its first argument as though it were a float, so it ends up reinterpreting some of the bytes in some way that happens to interpret them as a floating point number that's close to negative zero.

To fix this, either prototype the test function before calling it, or define it before you use it. Note that my compiler with the warnings cranked up explicitly tells you about the implicit declaration and the definition mismatch:

nodecl.c: In function ‘main’:
nodecl.c:4:3: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
   test(7.4, 4);
   ^
nodecl.c: At top level:
nodecl.c:8:6: warning: conflicting types for ‘test’
 void test(float height, float radius){
      ^
nodecl.c:4:3: note: previous implicit declaration of ‘test’ was here
   test(7.4, 4);
   ^

Going forward, if you see these warnings, you now know what they're talking about, and you should be able to diagnose your error more quickly.

Upvotes: 7

Related Questions