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 ofunion
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.