按位转换 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?
我正在从网络接收缓冲区,该缓冲区已转换为 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?