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

标签 c++ floating-point language-lawyer undefined-behavior

我已经构建了一个自定义版本的 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满足

the requirements of IEC 559 (IEEE 754) standard.



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

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

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

编辑:

正如评论中提到的,字节序可能是一个问题。有人知道吗?

最佳答案

"Does this mean that the bit operations I'm doing are well defined..."



TL;DR;,根据“定义良好”的严格定义: .

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

there are three floating point types: float, double, and long double. The type double provides at least as much precision as float, and the type long double provides at least as much precision as double. The set of values of the type float is a subset of the set of values of the type double; the set of values of the type double is a subset of the set of values of the type long double. The value representation of floating-point types is implementation-defined.


is_iec559条款仅符合:

True if and only if the type adheres to IEC 559 standard



如果一个文字 Sprite 给你写了一个糟糕的编译器,并制作了 float = binary16, double = binary32,和 long double = binary64,并制成 is_iec559对于所有类型,它仍然会遵守标准。

does that mean that I can extract exponent and mantissa in a well defined way?



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

假设您使用 float32_tis_iec559是真的,并且从所有规则中逻辑推导出它只能是没有陷阱表示的 binary32,并且您正确地论证了 memcpy是一个明确定义的相同宽度的算术类型之间的转换,不会破坏严格的别名。即使有所有这些假设,行为也可能被很好地定义,但只有可能并且不能保证您可以通过这种方式提取尾数。

IEEE 754 标准和 2 的补码考虑位串编码,以及 memcpy 的行为使用字节来描述。虽然假设 uint32_t 的位串是合理的和 float32_t将以相同的方式编码(例如相同的字节序),标准中没有保证。如果位串的存储方式不同,并且您对复制的整数表示进行移位和屏蔽以获得尾数,则答案将不正确,尽管 memcpy行为被明确定义。

As mentioned in the comments, endianess could be an issue. Does anybody know?



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

关于c++ - 如果 std::numeric_limits<float>::is_iec559 为真,这是否意味着我可以以明确定义的方式提取指数和尾数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58308042/

相关文章:

math - 浮点算术四舍五入到最接近的值

c# - float 范围内最大的不准确度是多少?

c# - 为什么 float.Parse ("123,45") 不抛出异常

c++ - char* 和 std::uint8_t* 之间的 reinterpret_cast - 安全吗?

c++ - 重载函数with (unsigned chars,传char,保证不编译?

c - 严格的别名规则背后的基本原理是什么?

c++ - Google模拟和替代关键字

c++ - 修改系统时钟时,boost::deadline_timer 可能会失败

c++ - 头文件和源文件 C++ 有什么区别?

c++ - 从编译器的角度来看,传递 const 值或 const ref 之间的区别