The number of bits for the mantissa, exponent and sign of a long double
value can be detected in the following way (supposing iec559):
template <typename T>
constexpr uint32_t bitsInExponent()
{
static_assert(std::numeric_limits<T>::is_iec559);
return std::ceil(std::log2(std::numeric_limits<T>::max_exponent-std::numeric_limits<T>::min_exponent 1));
}
// outputs 15 for long double on my machine/compiler
template <typename T>
constexpr uint32_t bitsInMantissa()
{
static_assert(std::numeric_limits<T>::is_iec559);
static_assert(std::numeric_limits<T>::radix == 2);
return std::numeric_limits<T>::digits-1;
}
// outputs 63 for long double on my machine/compiler
template <typename T>
constexpr uint32_t bitsInSign()
{
return 1;
}
Consequently, the total number of bits for the long double
is (on my machine and compiler) 79 (63 15 1), probably a IEEE-754 binary64 extended format.
However, the memory footprint of the long double
(sizeof(long double)
) is 16, thus 128 bits.
The padding of the float value (those 128-79, 49bits) seems to be at the higher bits on my machine with my compiler, and mostly filled with garbage.
My question is: Are those 1-63 bits always at the higher bits of the 128 bits?
In case the padding of the extended IEEE-754 binary-64 format is not guaranteed to be at the highest bits, how to detect where is the padding?
Try it: https://onlinegdb.com/doL6E7WYL
CodePudding user response:
The authoritative answer can only come from reading the ABI for your implementation.
However, if your long double
is 80-bit extended precision, then you are almost certainly on x86, since AFAIK it's the only major architecture with hardware support for that format. In that case, unless you have a very unusual compiler with a very unusual ABI, the data is going to be in the low 10 bytes, with the padding in the high 6 bytes. The x87 load and store instructions don't account for the padding, which is a more recent innovation to provide better alignment, so they will expect a pointer to the 10-byte data itself. By putting the data at the low end of the 16 bytes, a long double *
is a pointer to the data, and can be used directly with an x87 load or store. If it were anywhere else, every memory access to a long double
would require adding the appropriate offset, which would be inefficient.
Non-x86 platforms usually implement long double
in one of two ways:
Either it is just an alias for
double
(the C standard allows this), in which case it would normally be binary64 double precision with no padding, andsizeof(long double) == 8
. MSVC on x86-64 does this too, and doesn't give you access to the hardware's 80-bit extended precision support.Or, it is binary128 quadruple precision, in which case you have
sizeof(long double) == 16
and again no padding.