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.