Home > Blockchain >  printing the bits of float
printing the bits of float

Time:09-23

I know. I know. This question has been answered before, but I have a slightly different and a bit more specific question.

My goal is, as the title suggest, to cout the 32 bits sequence of a float.

And the solution provided in the previous questions is to use a union.

union ufloat{
   float f;
   uint32_t u;
};

This is all good and well. And I have managed to print the bits of a floating number.

union ufloat uf;
uf.f = numeric_limits<float>::max();
cout << bitset<32>(uf.f) << "\n"; // This give me 0x00000000, which is wrong.
cout << bitset<32>(uf.u) << "\n"; // This give me the correct bit sequence.

My question is why does bitset<32>(uf.f) doesn't work, but bitset<32>(uf.u) does?

The green-ticked anwser of this question Obtaining bit representation of a float in C says something about "type-punning", and I presume it's got something to do with that. But I am not sure how exactly.

Can someone please clearify? Thanks

CodePudding user response:

The constructor of std::bitset you are calling is:
constexpr bitset( unsigned long long val ) noexcept;

When you do bitset<32>(uf.f), you are converting a float to an unsigned long long value. For numeric_limits<float>::max(), that's undefined behavior because it's outside the range of the destination type, and in your case it happened to produce zero.

When you do bitset<32>(uf.u), you are again relying on undefined behavior, and in this case it happens to do what you want: convert a uint32_t that contains the bits of a float to an unsigned long long value.

In C , what you should do instead is use memcpy:

uint32_t u;
std::memcpy(&u, &uf.f, sizeof(u));
std::cout << std::bitset<32>(u) << "\n";

CodePudding user response:

This question is tagged C , but the linked one is about C.

Those are different languages, with different rules. In particular, as mentioned in the comments by Yksisarvinen:

Type punning is a way to read bytes of memory as if they were different type than they really are. This is possible to do through union in C, but in C reading anything but the last written to member of union is Undefined Behaviour.

Since C 20, we can use std::bit_cast.

auto f{ std::numeric_limits<float>::max() };  // -> 3.40282e 38
auto u{ std::bit_cast<unsigned>(f) };         // -> 7f7fffff

See e.g. https://godbolt.org/z/d9T3G6qGx.

  • Related