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?
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 float
s, 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With