Bri*_*oco 1 c++ floating-point unsigned numerical-methods
由于我的数值分析课程考试临近,我正在寻找在 C/C++ 中表示浮点数的实现代码?然后我在github上找到了一行代码。您能告诉我,下面代码片段中第二行的含义是什么,以及它为何如此重要?
float x = ...;
unsigned u = *(unsigned*)&x;
Run Code Online (Sandbox Code Playgroud)
unsigned只是缩写unsigned int并使用 C++ 风格的强制转换该行将转换为
unsigned int u = *reinterpret_cast<unsigned int*>(&x);
Run Code Online (Sandbox Code Playgroud)
但是请阅读下面为什么这在任何一种情况下都会导致未定义的行为。
(我建议不要使用问题中显示的行中的 C 风格转换,因为它们解析为哪种 C++ 风格转换并不明显。)
如果x是一个float变量,那么该行尝试将变量的对象表示重新解释float为 an 的对象表示unsigned int,基本上是将 的float内存重新解释为 an 的内存unsigned int,然后将unsigned int与该表示相对应的值存储在 中u。
步骤一步,&x是一个指向x类型的指针float*,reinterpret_cast<unsigned int*>(&x)是一个指向 的指针x,但现在是类型unsigned int*。然后*reinterpret_cast<unsigned int*>(&x)应该取消unsigned int*引用指向变量的指针,以从指向的内存位置float检索值,就好像存储在那里的字节代表一个值而不是一个值。最后应该使用该值来初始化它。unsigned intunsigned intfloatunsigned int u = u
float这会导致未定义的行为,因为通过指针访问对象是一种别名冲突unsigned int*。某些编译器具有可以启用此选项的选项(假设 和float具有unsigned int兼容的大小和对齐方式),但标准 C++ 语言本身不允许这样做。
一般来说,每当您看到reinterpret_cast(或可能解析为 a 的 C 风格转换reinterpret_cast)时,如果您不确切知道自己在做什么,则可能会导致未定义的行为。
从 C++20 开始,在没有未定义行为的情况下执行此操作的正确方法是使用std::bit_cast:
float x = /*...*/;
auto u = std::bit_cast<unsigned>(x);
Run Code Online (Sandbox Code Playgroud)
或在 C++20 之前使用std::memcpy:
float x = /*...*/;
unsigned u;
static_assert(sizeof(u) == sizeof(x));
std::memcpy(&u, &x, sizeof(u));
Run Code Online (Sandbox Code Playgroud)
尺寸验证是std::bit_cast自动完成的。static_assert即使没有 C++20,将and包装memcpy在类似的通用函数中以供重用也可能是个好主意。
这两者仍然要求 的表示x也是 a 的有效表示u。否则,行为仍然是未定义的。我不知道是否有任何 C++ 实现并不适用于这种float -> unsigned情况下的所有值。
另请注意:C 是一种不同的语言。C 中的规则可能很不同。例如,reinterpret_castC 中显然没有(unsigned*)可以解析的强制转换,并且对象模型非常不同。但在这种情况下,C 的别名规则将具有相同的效果。