UINT_MAX->float->uint32_t VC++ 结果为 0

UINT_MAX->float->uint32_t Results in 0 on VC++

作为将一些代码移植到 windows 的一些工作的一部分,我遇到了一个问题,其中 uint32_t 最大值 -> 浮动 -> uint32_t 的转换导致 0 时使用 Visual C++ 构建。 我将问题提炼为一个简单的程序,该程序在 运行 通过不同的编译器时显示了问题。

#include <iostream>
#include <limits>

using namespace std;

int main()
{
    uint32_t r1 = numeric_limits<uint32_t>::max();
    float r2 = static_cast<float>(numeric_limits<uint32_t>::max());
    uint32_t r3 = static_cast<uint32_t>(static_cast<float>(numeric_limits<uint32_t>::max()));


    cout << "r1 = " << r1 << endl
    << "r2 = " << r2 << endl
    << "r3 = " << r3 << endl;

    return 0;
}

当这是运行到http://webcompiler.cloudapp.net/ the output for r3 is 0 but when run through https://ideone.com/ylf74N时,r3是最大值。

我猜当它是 UINT_MAX 时,在转换回 uint32_t 的过程中发生了 +1,但我想知道是否有人知道为什么或到底发生了什么?

在您的系统上,float 似乎是 32 位类型,可能是 IEEE754(但请注意,标准并不坚持这一点)。

numeric_limits<uint32_t>::max() 是 4294967295.

这对于您的浮动来说太大了。 (一个IEEE754 32位浮点数所能容纳的最大奇数是8388607)。

因此您的号码四舍五入为 4294967296。(这是可容纳的壁橱号码)。

当然,将其转换回 uint32_t 会使其回绕到 0。

至于另一个系统,由于 sizeof 输出,我们知道 float 是 32 位。编译器正在错误地进行优化。

当从整数类型转换为实数类型时,结果可能不精确,并四舍五入到最接近实数类型的可表示值(高于或低于原始值)。在您的情况下,浮点数的尾数部分有 24 位,因此 0xFFFFFFFF 不会在转换后保持不变,因为它需要 32 位尾数。因此,它可以转换为 0x100000000(最接近的)。如果发生这种情况,您会将 0x100000000 从 float 转换为 32 位无符号类型。并且此转换导致 未定义的行为 (包括 unpredictable/unreliable 结果),因为该值超出了 32 位无符号类型的范围。

您还通过其他编译器获得了 UB,只是您没有意识到而已。通过引入 volaitle(在 r1、r2 和 r3 的声明中)来规避优化,您将看到它 (ideone.com/1CCat8).