Reputation: 557
Why this program is giving unexpected numbers(ex: 2040866504
, -786655336
)?
#include <stdio.h>
int main()
{
int test = 0;
float fvalue = 3.111f;
printf("%d", test? fvalue : 0);
return 0;
}
Why it is printing unexpected numbers instead of 0
? should it supposed to do implicit typecast? This program is for learning purpose nothing serious.
Upvotes: 13
Views: 1196
Reputation: 106112
Because you are using %d
for printing a float
value. Use %f
. Using %d
to print a float
value invokes undefined behavior.
EDIT: Regarding OP's comments;
Why it is printing random numbers instead of 0?
When you compile this code, compiler should give you a warning:
[Warning] format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat]
This warning is self explanatory that this line of code is invoking an undefined behavior. This is because, the conversion specification %d
specifies that printf
is to convert an int
value from binary to a string of decimal digits, while %f
does the same for a float
value. On passing the fvalue
compiler know that it is of float
type but on the other hand it sees that printf
expects an argument of type int
. In such cases, sometimes it does what you expect, sometimes it does what I expect. Sometimes it does what nobody expects (Nice Comment by David Schwartz).
See the test cases 1 and 2. It is working fine with %f
.
should it supposed to do implicit typecast?
No.
Upvotes: 11
Reputation: 58647
When we have the expression E1 ? E2 : E3
, there are four types involved. Expressions E1
, E2
and E3
each have a type (and the types of E2
and E3
can be different). Furthermore, the whole expression E1 ? E2 : E3
has a type.
If E2
and E3
have the same type, then this is easy: the overall expression has that type. We can express this in a meta-notation like this:
(T1 ? T2 : T2) -> T2
"The type of a ternary expression whose alterantives are both of the same type T2
is just T2."
If they don't have the same type, things get somewhat interesting, and the situation is quite similar to E2
and E3
being involved together in an arithmetic operation. For instance if you add together an int
and float
, the int
operand is converted to float
. That is what is happening in your program. The type situation is:
(int ? float : int) -> float
the test fails, and so the int
value 0
is converted to the float
value 0.0
.
This float
value is not compatible with the %d
conversion specifier of printf
, which requires an int
.
More precisely, the float
value undergoes one more. When a float
is passed as one of the trailing arguments to a variadic function, it is converted to double
.
So in fact the double
value 0.0 is being passed to printf
where it expects int
.
In any case, it is undefined behavior: it is nonportable code for which the ISO standard definition of the C language doesn't offer a meaning.
From here on, we can apply platform-specific reasoning why we don't just see 0
. Suppose int
is a 32 bit, four byte type, and double
is the common 64 bit, 8 byte, IEE754 representation, and that an all-bits-zero is used for 0.0
. So then, why isn't a 32 bit portion of this all-bits-zero treated by printf
as the int
value 0
?
Quite possibly, the 64 bit double
argument value forces 8 byte alignment when it is put onto the stack, possibly moving the stack pointer by four bytes. And then printf
pulls out garbage from those four bytes, rather than zero bits from the double
value.
Upvotes: 1
Reputation: 10355
Although the existing upvoted answers are correct, I think they are far too technical and ignore the logic a beginner programmer might have:
Let's look at the statement causing confusion in some heads:
printf("%d", test? fvalue : 0);
^ ^ ^ ^
| | | |
| | | - the value we expect, an integral constant, hooray!
| | - a float value, this won't be printed as the test doesn't evaluate to true
| - an integral value of 0, will evaluate to false
- We will print an integer!
What the compiler sees is a bit different. He agrees on the value of test
meaning false
. He agrees on fvalue
beeing a float
and 0
an integer. However, he learned that the different possible outcomes of the ternary operator must be of same type! int
and float
aren't. In this case, "float
wins", 0
becomes 0.0f
!
Now printf
isn't type safe. This means you can falsely say "print me an integer" and pass an float without the compiler noticing. Exactly that happened. No matter what the value of test
is, the compiler deduced that the result will be of type float
. Hence, your code is equivalent to:
float x = 0.0f;
printf("%d", x);
At this point, you experience undefined behaviour. float
simply isn't something integral
what is expected by %d
.
The observed behaviour is dependent on the compiler and machine you're using. You may see dancing elephants, although most terminals don't support that afaik.
Upvotes: 8
Reputation: 182885
Most likely, your platform passes floating point values in a floating point register and integer values in a different register (or on the stack). You told printf
to look for an integer, so it's looking in the register integers are passed in (or on the stack). But you passed it a float
, so the zero was placed in the floating point register that printf
never looked at.
The ternary operator follows language rules to decide the type of its result. It can't sometimes be an integer and sometimes be a float. Those could be different sizes, stored in different places, and so on, which would make it impossible to generate sane code to handle both possible result types.
This is a guess. Perhaps something completely different is happening. Undefined behavior is undefined for a reason. These kinds of things can be impossible to predict and very difficult to understand without lots of experience and knowledge of platform and compiler details. Never let someone convince you that UB is okay or safe because it seems to work on their system.
Upvotes: 18