Reputation: 41
I understand that we assign an unsigned long long
because it contains the same number of bits as a float
. But what exactly does i
stand for?
My best interpretation is that we are trying to cast the double number to an unsigned long long int
because they have the same number of bits. And it appears that the best way to do so is to dereference the pointer we have set up?
void printDouble(double d) {
unsigned long long int i = *(unsigned long long int *)&d;
for (int j = 63; j >= 0; j--) {
std::cout << ((i >> j) & 1);
if (j == 63) std::cout << " ";
else if (j == 52) std::cout << " ";
}
}
Upvotes: 2
Views: 1931
Reputation: 11
Since C++20 std::bit_cast can be used:
std::uint64_t bits = std::bit_cast<std::uint64_t>(d);
Upvotes: 1
Reputation: 3256
On most (but not all) platforms, float
and double
use IEEE 754 representation to approximate real valued numbers. For the meaning of the bits, see e.g. https://en.wikipedia.org/wiki/IEEE_754. Briefly, in an IEEE-754 64-bit double, there is a sign bit, an 11-bit exponent, and a 52-bit significand (aka mantissa) to represent the real number in a form like
sign ⋅ 2^(exponent − 1023) ⋅ (1.0 + significand / 2^52)
There are special cases beyond this (infinities, NaN, subnormals, signed zeros), but that's the essential idea. If you are looking for a deeper reference, a great book is Michael L. Overton, "Numerical Computing with IEEE Floating Point Arithmetic".
Beware that dereferencing *(unsigned long long int *)&d
is undefined behavior, as others have commented. If you really want to get the bits of a double into a 64-bit int, a safe way to do that is through memcpy:
uint64_t bits;
std::memcpy(&bits, &d, sizeof(d));
Upvotes: 7