How can a union be used to read the raw bit pattern of a float, and why doesn't a plain cast work?
Write the value through the float member, then read it back through the unsigned int member — since both share the same bytes, you get the float's exact bit pattern.
A union member overlays the same storage, so storing a float and reading an unsigned int reinterprets those 4 bytes without converting the value:
unsigned int get_bitpattern(float f) {
union { float f; unsigned int u; } fu;
fu.f = f; // write the bits as a float
return fu.u; // read the SAME bits as an integer
}
Because no conversion happens, the compiled code is trivial — the 4 bytes are already in place:
get_bitpattern:
movl 4(%esp), %eax # just return the incoming 4 bytes unchanged
ret
Result: 3.1415927410 = 01000000010010010000111111011011b = 0x40490fdb (the IEEE-754 single-precision encoding of π).
Why a cast fails: (unsigned int)f performs a value conversion — it truncates 3.14… to the integer 3 (0x00000003), throwing away the bit pattern entirely. The union reinterprets the bits; the cast converts the number. They are completely different operations.
Modern note: the strictly-portable, defined-behavior way to do this today is memcpy (or C++20 std::bit_cast); the union trick is the classic and works in practice on common compilers.
Go deeper:
Type punning (Wikipedia) — type punning via unions versus a value-converting cast.