按位转换 uint32_t 以在 C/C++ 中浮动

bitwise casting uint32_t to float in C/C++

我正在从网络接收缓冲区,该缓冲区已转换为 32 位字数组。我有一个词被我的接口文档定义为 IEEE-754 浮点数。我需要从缓冲区中提取这个词。在不调用转换的情况下很难从一种类型转换为另一种类型。这些位已经符合 IEEE-754 浮点标准,我不想重新安排任何位。

我的第一个尝试是将 uint32_t 的地址转换为 void*,然后将 void* 转换为 float*,然后解引用为 float:

float ieee_float(uint32_t f)
{
    return *((float*)((void*)(&f)));
}

error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]

我的第二次尝试是这样的:

float ieee_float(uint32_t f)
{
    union int_float{
        uint32_t i;
        float f;
    } tofloat;

    tofloat.i = f;
    return tofloat.f;
}

然而,街上的消息是工会是完全不安全的。从不是最近写入的联合成员中读取是未定义的行为。

所以我尝试了一种更 C++ 的方法:

float ieee_float(uint32_t f)
{
  return *reinterpret_cast<float*>(&f);
}

error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]

我的下一个想法是“去他的。我为什么还要处理指针?”刚刚尝试过:

float ieee_float(uint32_t f)
{
  return reinterpret_cast<float>(f);
}

error: invalid cast from type ‘uint32_t {aka unsigned int}’ to type ‘float’

有没有办法在不触发 warning/error 的情况下进行转换?我正在使用 -Wall -Werror 使用 g++ 进行编译。我不想触及编译器设置。

我标记了 C,因为 c 解是可以接受的。

如前所述,您有多种选择here:

  • 使用 union 解决方案:因为 C11 明确允许(如其他答案所述)。
  • 不要使用 32 位字数组,而是使用 8 位字数组 (uint8_t),因为 char 类型可以别名为任何类型。

在C++20中,可以使用std::bit_cast:

float ieee_float(uint32_t f)
{
    return std::bit_cast<float>(f);
}

在 C++17 及之前的版本中,正确的方法™ 是:

float ieee_float(uint32_t f)
{
    static_assert(sizeof(float) == sizeof f, "`float` has a weird size.");
    float ret;
    std::memcpy(&ret, &f, sizeof(float));
    return ret;
}

-O1 及更高版本的 GCC 和 Clang 都为此代码生成相同的程序集和一个原始的 reinterpret_cast<float &>(f)(但后者是未定义的行为,在某些情况下可能无法工作)。

没有 C/C++ 语言。它们是具有不同规则的不同语言。 C 中的有效方法是使用联合,但这在 C++ 中是不允许的。见

  • Unions and type-punning
  • Is type-punning through a union unspecified in C99, and has it become specified in C11?

在旧的 C++ 标准中,您必须使用 std::memcpy。甚至 reinterpret_cast 类型双关 invokes undefined behavior, hence disallowed. In C++20 a new cast type called std::bit_cast 正是为此目的而创建的

float ieee_float(uint32_t f)
{
  return std::bit_cast<float>(f);
}

另请参阅:

  • What's a proper way of type-punning a float to an int and vice-versa?