如果 std::numeric_limits<float>::is_iec559 为真,这是否意味着我可以以明确定义的方式提取指数和尾数?

If std::numeric_limits<float>::is_iec559 is true, does that mean that I can extract exponent and mantissa in a well defined way?

本文关键字:定义 方式 提取 我可以 指数 lt float limits numeric std gt      更新时间:2023-10-16

我已经构建了frexp的自定义版本:

auto frexp(float f) noexcept
{
static_assert(std::numeric_limits<float>::is_iec559);
constexpr uint32_t ExpMask = 0xff;
constexpr int32_t ExpOffset = 126;
constexpr int MantBits = 23;
uint32_t u;
std::memcpy(&u, &f, sizeof(float)); // well defined bit transformation from float to int
int exp = ((u >> MantBits) & ExpMask) - ExpOffset; // extract the 8 bits of the exponent (it has an offset of 126)
// divide by 2^exp (leaving mantissa intact while placing "0" into the exponent)
u &= ~(ExpMask << MantBits); // zero out the exponent bits
u |= ExpOffset << MantBits; // place 126 into exponent bits (representing 0)
std::memcpy(&f, &u, sizeof(float)); // copy back to f
return std::make_pair(exp, f);
}

通过检查is_iec559我确保float满足

IEC 559 (IEEE 754( 标准的要求。

我的问题是:这是否意味着我正在做的位操作是明确定义的并且可以做我想做的?如果没有,有没有办法解决它?

我测试了一些随机值,它似乎是正确的,至少在使用 msvc 编译的 Windows 10 和 wandbox 上。但是请注意,(故意(我不是在处理次正常、NaNinf的边缘情况。

如果有人想知道我为什么要这样做:在基准测试中,我发现这个版本的frexp比 Windows 15 上的std::frexp快 10 倍。我还没有测试过其他平台。但我想确保这不仅仅是巧合,而且将来可能会刹车。

编辑:

正如评论中提到的,恩迪亚内斯可能是一个问题。有人知道吗?

"这是否意味着我正在执行的位操作已明确定义......">

TL;DR;,根据"明确定义"的严格定义:

您的假设可能是正确的,但定义不明确,因为无法保证位宽或float的实现。从§ 3.9.1:

有三种浮点类型:浮点型、双精度型和长型双精度型。双精度型至少提供与浮点数一样多的精度,而长型双精度型提供的精度至少与双精度一样多。浮点型的值集是双精度类型的值集的子集;双精度类型的值集是长双精度类型的值集的子集。浮点类型的值表示形式是实现定义的。

is_iec559条款仅符合以下条件:

且仅当类型符合 IEC 559 标准时为 True

如果一个文字精灵给你写了一个糟糕的编译器,并做了float=binary16,double=binary32,long double=binary64,并且对所有类型都is_iec559正确,它仍然会遵守标准。

这是否意味着我可以用明确定义的方式提取指数和尾数?

TL;DR;,由C++标准的有限保证:

假设您使用float32_t并且is_iec559为真,并且从所有规则中逻辑推断出它只能是二进制32,没有陷阱表示,并且您正确地认为memcpy是相同宽度的算术类型之间的转换的良好定义,并且不会破坏严格的混叠。即使有所有这些假设,行为也可能被很好地定义,但只是可能且不能保证你可以通过这种方式提取尾数。

IEEE 754 标准和 2 的补码涉及位字符串编码memcpy的行为使用字节描述。虽然假设uint32_tfloat32_t的位字符串将以相同的方式编码(例如相同的字节序(是合理的,但在标准中无法保证这一点。如果位字符串的存储方式不同,并且您移动并屏蔽复制的整数表示以获得尾数,则答案将不正确,尽管memcpy行为已明确定义。

正如评论中提到的,恩迪亚内斯可能是一个问题。有人知道吗?

至少有一些体系结构对浮点寄存器和整数寄存器使用了不同的字节序。同样的链接说,除了小型嵌入式处理器,这不是一个问题。我完全信任维基百科的所有主题,并拒绝做任何进一步的研究。