garvit vijai
garvit vijai

Reputation: 21

Explicit Type Casting in C

What's the difference between the following code snippets:

  1. double a = 10.154430;
    int b = a;
    printf("%d", b); //prints 10 (WHY?)
    
  2. 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

Answers (6)

InfernApe
InfernApe

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

John Bollinger
John Bollinger

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 floats 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

selbie
selbie

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

haccks
haccks

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

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

Jorvis
Jorvis

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

Related Questions