Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 244
Alfie Avatar asked Dec 24 '22 00:12

Alfie


2 Answers

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.

like image 183
templatetypedef Avatar answered May 10 '23 23:05

templatetypedef


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.

like image 33
Marco Guerri Avatar answered May 10 '23 23:05

Marco Guerri