【发布时间】:2014-01-12 19:37:43
【问题描述】:
假设我保证 float 是 IEEE 754 binary32。给定一个对应于有效浮点数的位模式,存储在std::uint32_t 中,如何以最有效的标准兼容方式将其重新解释为float?
float reinterpret_as_float(std::uint32_t ui) {
return /* apply sorcery to ui */;
}
我有几种方法我知道/怀疑/假设有一些问题:
-
通过
reinterpret_cast,float reinterpret_as_float(std::uint32_t ui) { return reinterpret_cast<float&>(ui); }或等效
float reinterpret_as_float(std::uint32_t ui) { return *reinterpret_cast<float*>(&ui); }存在别名问题。
-
通过
union,float reinterpret_as_float(std::uint32_t ui) { union { std::uint32_t ui; float f; } u = {ui}; return u.f; }这实际上是不合法的,因为它只允许从最近写给成员的内容中读取。然而,似乎有些编译器 (gcc) 允许这样做。
-
通过
std::memcpy,float reinterpret_as_float(std::uint32_t ui) { float f; std::memcpy(&f, &ui, 4); return f; }哪个 AFAIK 是合法的,但是复制单个单词的函数调用似乎很浪费,尽管它可能会被优化掉。
-
通过
reinterpret_casting 到char*并复制,float reinterpret_as_float(std::uint32_t ui) { char* uip = reinterpret_cast<char*>(&ui); float f; char* fp = reinterpret_cast<char*>(&f); for (int i = 0; i < 4; ++i) { fp[i] = uip[i]; } return f; }哪个 AFAIK 也是合法的,因为
char指针不受别名问题的影响,并且手动字节复制循环节省了可能的函数调用。循环肯定会被展开,但是 4 个可能单独的一字节加载/存储令人担忧,我不知道这是否可优化为单个四字节加载/存储。
4 是我能想到的最好的。
到目前为止我是对的吗?有没有更好的方法来做到这一点,特别是可以保证单次加载/存储的方法?
【问题讨论】:
-
位模式从何而来?
-
memcpy看起来 像一个函数调用,在优化的代码中尝试一下,看看结果,它可能会让你大吃一惊。 4 违反了严格的别名规则(你可以别名为char*用于读取,但不能用于写入) -
@DavidRodríguez-dribeas:关于 #4:真的吗?
-
@Manu343726 因为这不是 reinterpret_cast 的工作方式。
reinterpret_cast<T&>(e)相当于*reinterpret_cast<T*>(&e)根据 5.2.10/11 -
@willj 根据 5.2.10/7 这实际上与 reinterpret_cast 完全相同
标签: c++ c++11 type-conversion language-lawyer standards-compliance