Reputation: 21
What's the difference between the following code snippets:
double a = 10.154430;
int b = a;
printf("%d", b); //prints 10 (WHY?)
double a = 10.154430;
printf("%d", a); //prints garbage value as expected`
Now, in both cases, I'm not doing explicit type conversion but case I work correctly..why?(int is storing double value, which shouldn't work implicitly)
Upvotes: 1
Views: 1719
Reputation: 1
Any variable (be it int, char, float or double) is stored as bits at the memory location allocated to it.
printf("%d", a);
What this line of code says is... Print the bits at memory address a
as %d
(int).
For example, consider the below lines:
char ch = '0';
printf("%d", ch);
The first line char ch = 0;
stores the binary of '0', which is 0b110000 at address named as ch
.
The second line printf("%d", ch);
prints the value at address ch
as %d
. Basically it prints the value '0b110000' as an int, which translates to 48 in decimal. Hence the output will be 48.
Similarly for 'double', the bits stored at address of the 'double' variable will be printed as 'int' and hence the garbage value. The fractional part of 'double' is also stored as bits but an 'int' type, will not see those bits as fractional part. You could read more about how 'floats' and 'doubles' are stored.
Upvotes: 0
Reputation: 180058
Now, in both cases, I'm not doing explicit type conversion but case I work correctly..why?(int is storing double value, which shouldn't work implicitly)
Case 1 involves using the simple assignment operator. The operands satisfy this operator's constraints, specifically utilizing the case
the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type
Note well that the types of the two arguments are not required to be the same. It is sufficient for them to both be arithmetic types. With that being the case, the primary aspect of the specified behavior is
the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.
(Emphasis added.) Thus, you get a conversion even without an explicit cast.
Case 2 is different. Note well the prototype for printf
:
int printf(const char * restrict format, ...);
It is a variadic function, and you're passing the double
among the variadic arguments. Now, when you pass an argument to a prototyped function parameter, you get exactly the same kind conversions that you do in simple assignment, but for arguments to functions with no in-scope prototype and for the variable arguments to variadic functions, you get the "default argument promotions". These consist only of promoting integer types smaller than int
to int
or unsigned int
, and promoting float
s to double
.
In the specific case of printf()
, the behavior is undefined when an actual argument does not match the corresponding field directive. This is your case. More generally, UB occurs for any variadic function when it makes an attempt to interpret one of its variable arguments as being of a type incompatible with that argument's (default-promoted) actual type.
Upvotes: 0
Reputation: 104474
When printf("%d", a)
is invoked, the following is pushed onto the call stack. Let's assume that sizeof(int)==4
and sizeof(double)==8
.
"%d" // address of string literal
a // 8 bytes pushed onto stack assuming sizeof(double) is 8
return address of caller
Then a jump to the printf
function itself is invoked.
When printf starts, it has the first parameter, the format string ("%d"
) at a known stack offset, but it doesn't know how many parameters were also pushed onto the stack. It relies on the format codes to tell it how to interpret the subsequent bytes on the stack.
It parses the format string and reads the %d
. And then upon seeing %d
it assumes that the next value on the stack that was pushed is a 4-byte integer. It then reads 4 bytes off the stack and does whatever processing it needs to print that memory as an integer. But in fact, what it's really printing is half the bytes that makes up a floating point value.
Looking purely at an Intel example, assume the following code.
double d = 3.14;
printf("%x\n", d); // push 8-byte double, but print as integer hex
return 0;
Prints out: 51eb851f
The IEEE floating point representation of 3.14 as stored in memory by an Intel processor is this: 1f 85 eb 51 b8 1e 09 40
. The first 4 bytes of the double are the same as what was printed. (Except in reverse byte order because Intel is "little endian").
Upvotes: 0
Reputation: 105992
In first case there is an implicit type conversion. The statement
int b = a; // Implicit conversion. OK
is equivalent to
int b = (int)a; // Explicit conversion. OK
(The above type conversions are OK as long as type converted value fits within the range of int
type.)
While in second case your program is invoking undefined behavior by not using a correct format specification for double
data type. The garbage value you are getting is one possible result of undefined behavior.
Upvotes: 1
Reputation: 1
Notice that the first case is equivalent to printf("%d", (int)a);
Casts from double
to int
are sort-of special cases, they are converting a floating point to an integer.
(In practice, most other casts, e.g. casts between various integral types like unsigned long
and short
, are "keeping" most of the bits of the internal representations; in that aspect, cast from integral to floating types or vice versa are really special, since involving some additional processing)
Your second case printf("%d", a)
is actually undefined behavior (UB). You are using printf
with an argument of type incompatible with its control format string. UB can be really worse.
See also §6.5.4 of n1570
Upvotes: 3
Reputation: 386
There is a difference in code snippet 1 and code snippet 2, that is :
-> In code snippet 1 the format specifier %d expects an Integer type and you are referencing b which is of type int.
-> But, in code snippet 2 you are referencing d which of type of type double but the format specifier %d expects a type int
Upvotes: 1