3
#include<stdio.h>

union U{
    struct{
        int x;
        int y;
    };
    float xy;
};

int main(){
    union U u;
    u.x = 99;
    printf("xy %f\n",u.xy); //output " 0 "
    return 0;
}

I have figured out that it has something to do with how float is stored and read internally. Can someone explain it to me exactly?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
chiru player
  • 301
  • 3
  • 10
  • 3
    Printing with `%f` is not very useful; you should consider `%g` or `%e`. If the value is very small, it will print as `0.000000` even when it is not zero. (For example, any value smaller than `0.0000005` will be printed as `0.000000`.) You need to read about [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) at Wikipedia, for example, to find out about how such values are represented. For example, on a Mac running macOS Sierra 10.12.5 using GCC 7.2.0, using `printf("xy %22.16g\n", u.xy);` produces `xy 1.387285479681569e-43`. – Jonathan Leffler Aug 25 '17 at 04:37
  • @JonathanLeffler; OP has initialized member `x` of unnamed struct of union `u`. Then he is using a different union member `xy` to read the contents. There is a possibility that it might be a trap representation and in this case behavior of the code will be undefined. – haccks Aug 25 '17 at 04:43
  • The range of normal numbers in 4-byte `float` is normally 10⁺³⁸ to 10⁻³⁸, so a value 1.387…E-43 from a `float` is a subnormal value (though well within range of 8-byte `double` values). – Jonathan Leffler Aug 25 '17 at 04:54
  • @chiru you did not initialize variable y, so it can have any value. – MCG Aug 25 '17 at 05:11
  • 1
    @MCG variable `y` is not accessed in this code, if `sizeof(float) <= sizeof(int)` – M.M Aug 25 '17 at 05:22
  • Afaik, this violates strict aliasing rules, so the compiler is allowed to assume that `.xy` is used uninitialized, and that the write to `.x` is effectless. Thus, it may compile the code equivalent to `int main(){ printf("xy %f\n", 0.0); }` completely optimizing the use of the union away. I believe, the only safe way to reinterpret the bits of an `int` as a `float` is to use `memcpy()`: `int x = 99; float xy; memcpy(&xy, &x, sizeof(float));` – cmaster - reinstate monica Aug 25 '17 at 11:00

4 Answers4

1

Converting comments into an answer.

Printing with %f is not very useful; you should consider %g or %e. With %f, if the value is very small, it will be printed as 0.000000 even when it is not zero. (For example, any value smaller than 0.0000005 will be printed as 0.000000.) You need to read about IEEE 754 at Wikipedia, for example, to find out about how such values are represented.

For example, on a Mac running macOS Sierra 10.12.5 using GCC 7.2.0, printing with:

printf("xy %22.16g\n", u.xy);

produces:

 xy 1.387285479681569e-43

The range of normal numbers in 4-byte float is normally 10⁺³⁸ to 10⁻³⁸, so a value 1.387…E-43 from a float is a subnormal value (though well within range of 8-byte double values). Remember that float values passed to printf() are promoted to a double automatically because of 'default argument promotions' — printf() never actually receives a float value.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

The way float is represented is impacting the result. See How to represent FLOAT number in memory in C and https://softwareengineering.stackexchange.com/questions/215065/can-anyone-explain-representation-of-float-in-memory to see how the float is represented in the memory. You also did not initialize structure variable y. It can have any value in it. It may or may not be used. In your case, the value is very small and you are not printing full value. To see the value of float xy you need to print full value. As suggested in a comment here, if I use below statement in Codeblocks 16.1 on Windows( which contains MinGW compiler) I get value different than 0.000000.

printf("xy %.80f\n",u.xy);

gives me xy 0.00000000000000000000000000000000000000000013872854796815689002144922874570169700

MCG
  • 1,011
  • 1
  • 10
  • 21
0

Yes, you're right, it has to do with the binary representation of a float value, which is defined in the standard document IEEE 754. See this great article by Steve Hollasch for an easy explanation. A float is a 32-bit value, and so is an int. So in your union U, xy falls exactly on the x member of the embedded struct, so when you set x to 99, the bits 1100011 (binary representation of 99) will be reinterpreted in xy as the mantissa of a float. As others have pointed out, this is a very small value, which may be printed as 0, depending on the printf format specifier.

Guessing from the naming of your union members (x, y, xy), I think you wanted to declare something different, e.g.:

union U
    {
    struct
        {
        short x;
        short y;
        };
    float xy;
    };

Or:

union U
    {
    struct
        {
        int x;
        int y;
        };
    double xy;
    };

In those declarations, both the x and y members are mapped onto the xy member.

SBS
  • 806
  • 5
  • 13
-1

The reason is that the value is very close to zero, so the default 6 digits of precision isn't enough to display anything.

Try:

union { int i; float f; } u = {.i= 99};

printf("f %g\n", u.f);
o11c
  • 15,265
  • 4
  • 50
  • 75