Reputation: 85
The below code results in:
0.000000
10
What is being returned from 'data' in this case? I know n.data.idata
and n.data.fdata
would be the correct usage, I'm just curious as to why the integer value works in this case and the float value does not.
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
union container
{
int idata;
float fdata;
} data;
struct node *next;
} Node;
int main()
{
Node i = {.data.idata = 10};
Node n = {.data.fdata = 10.0};
printf("%f\n", n.data);
printf("%d\n", i.data);
printf("\nExiting program...\n");
return 0;
}
Upvotes: 5
Views: 3141
Reputation: 11648
Apologies for my original answer to the wrong question. I think this answer might help clarify for people coming to this question who are new to C what 2501 is talking about (emphasis mine)
Specifier f assumes a default argument promotion from float to double, which doesn't happen in this case, because a union data is passed to the function. So the function receives the union data which consists of 4 bytes and represents a float, but tries to print 8 bytes, because it expects a double. The result is a nonsense value, in your case 0.0.
Type promotion converts one binary representation to another safely or at least in a standardized way. The following was all done on an x86 machine.
The value in binary of 10
in unsigned 64 bit format
|01010000|00000000|00000000|00000000|00000000|00000000|00000000|00000000
Binary value of 10 as a 32 bit float...
|00000000|00000000|00000100|10000010
The value of 10 as a double in 64 bit format...
|00000000|00000000|00000000|00000000|00000000|00000000|00100100|00000010
All three representations of the number 10 differ significantly. Type promotion
not happening means that the number in the printf
statement was seen as a
double and it was printed as such. In this case the only reason it showed up as
a zero was precision.
The Node
data structure in the following code is 8 bytes. There is no padding
in the struct or it could not be 8 bytes long. If we set the float
to 10 and next
to 0 we have this representation in memory...
|00000000|00000000|00000100|10000010|00000000|00000000|00000000|00000000
The above if converted to a double and printed with printf("%f'\n");
makes it
look like a 0.0
. If you print it using 'printf("%g\n")' you wqould see that some
bits must be set in the 8 bytes, in my case I got
5.39824e-315
To see what the floats class can use fpclassify
. See code below...
#include <assert.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node {
union container {
float fdata;
int idata;
} data;
uint32_t next;
} Node;
int main(void) {
size_t nsize1 = sizeof(Node);
assert(nsize1 == 8);
assert(sizeof(int) == 4);
assert(sizeof(float) == 4);
assert(sizeof(double) == 8);
Node n = {.data.fdata = 10.0};
n.next = 0;
double d = *(double*)&n;
int class_of_d = fpclassify(d);
assert(d > 0);
switch(class_of_d) {
case FP_NAN : printf("FP_NAN\n");break;
case FP_INFINITE : printf("FP_INFINITE\n");break;
case FP_ZERO : printf("FP_ZERO\n");break;
case FP_NORMAL : printf("FP_NORMAL\n");break;
case FP_SUBNORMAL : printf("FP_SUBNORMAL\n");break;
case FP_SUPERNORMAL: printf("FP_SUPERNORMAL\n");break;
}
printf("%g\n", d);
return 0;
}
Upvotes: -1
Reputation: 25752
Let's ignore the obvious undefined behavior for a moment, which happens because an incorrect type is passed to the printf function for the specifier f
. That type being an anonymous union.
Specifier f
assumes a default argument promotion from float to double, which doesn't happen in this case, because a union data
is passed to the function. So the function receives the union data
which consists of 4 bytes and represents a float, but tries to print 8 bytes, because it expects a double. The result is a nonsense value, in your case 0.0.
(This answer assumes IEEE 754, and sizeof(int)<=4)
Upvotes: 5