如何在 C++ 中获取 long double 值的填充位置?
How to get the padding position of a long double value in C++?
一个long double
值的尾数、指数和符号的位数可以通过以下方式检测(假设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;
}
因此,long double
的总位数是(在我的机器和编译器上)79 (63+15+1),可能是 IEEE-754 二进制 64 扩展格式。
然而,long double
(sizeof(long double)
) 的内存占用是 16,因此是 128 位。
浮点值的填充(那些 128-79、49 位)似乎在我的机器上用我的编译器填充了较高的位,并且大部分充满了垃圾。
我的问题是:那些1-63位总是在128位的高位吗?
如果不能保证扩展 IEEE-754 二进制 64 格式的填充位于最高位,如何检测填充位置?
权威的答案只能来自阅读 ABI 以供您实施。
但是,如果您的 long double
是 80-bit extended precision,那么您几乎可以肯定是在 x86 上,因为据我所知,它是唯一具有硬件支持该格式的主要架构。在那种情况下,除非你有一个非常不寻常的编译器和一个非常不寻常的 ABI,否则数据将位于低 10 字节,填充在高 6 字节。 x87 加载和存储指令不考虑填充,这是提供更好对齐的最新创新,因此它们将期望指向 10 字节数据本身的指针。通过将数据放在 16 字节的低端,long double *
是指向数据的指针,可以直接用于 x87 加载或存储。如果它在其他任何地方,每次对 long double
的内存访问都需要添加适当的偏移量,这将是低效的。
非 x86 平台通常通过以下两种方式之一实现 long double
:
要么它只是 double
的别名(C++ 标准允许这样做),在这种情况下它通常是没有填充的 binary64 双精度,并且 sizeof(long double) == 8
. MSVC++ on x86-64 does this too,并且不允许您访问硬件的 80 位扩展精度支持。
或者,它是 binary128 四倍精度,在这种情况下你有 sizeof(long double) == 16
并且再次没有填充。
一个long double
值的尾数、指数和符号的位数可以通过以下方式检测(假设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;
}
因此,long double
的总位数是(在我的机器和编译器上)79 (63+15+1),可能是 IEEE-754 二进制 64 扩展格式。
然而,long double
(sizeof(long double)
) 的内存占用是 16,因此是 128 位。
浮点值的填充(那些 128-79、49 位)似乎在我的机器上用我的编译器填充了较高的位,并且大部分充满了垃圾。
我的问题是:那些1-63位总是在128位的高位吗?
如果不能保证扩展 IEEE-754 二进制 64 格式的填充位于最高位,如何检测填充位置?
权威的答案只能来自阅读 ABI 以供您实施。
但是,如果您的 long double
是 80-bit extended precision,那么您几乎可以肯定是在 x86 上,因为据我所知,它是唯一具有硬件支持该格式的主要架构。在那种情况下,除非你有一个非常不寻常的编译器和一个非常不寻常的 ABI,否则数据将位于低 10 字节,填充在高 6 字节。 x87 加载和存储指令不考虑填充,这是提供更好对齐的最新创新,因此它们将期望指向 10 字节数据本身的指针。通过将数据放在 16 字节的低端,long double *
是指向数据的指针,可以直接用于 x87 加载或存储。如果它在其他任何地方,每次对 long double
的内存访问都需要添加适当的偏移量,这将是低效的。
非 x86 平台通常通过以下两种方式之一实现 long double
:
要么它只是
double
的别名(C++ 标准允许这样做),在这种情况下它通常是没有填充的 binary64 双精度,并且sizeof(long double) == 8
. MSVC++ on x86-64 does this too,并且不允许您访问硬件的 80 位扩展精度支持。或者,它是 binary128 四倍精度,在这种情况下你有
sizeof(long double) == 16
并且再次没有填充。