浮点碎片和严格的别名
-
29-09-2019 - |
题
我试图从浮子中提取钻头,而无需调用未定义的行为。这是我的第一个尝试:
unsigned foo(float x)
{
unsigned* u = (unsigned*)&x;
return *u;
}
据我了解,由于严格的混叠规则,这不能保证能够起作用,对吗?如果与角色指针采取中间步骤,它是否有效?
unsigned bar(float x)
{
char* c = (char*)&x;
unsigned* u = (unsigned*)c;
return *u;
}
还是我必须自己提取个人字节?
unsigned baz(float x)
{
unsigned char* c = (unsigned char*)&x;
return c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
}
当然,这是依赖尼亚尼斯的缺点,但我可以忍受。
工会黑客肯定是不确定的行为,对吗?
unsigned uni(float x)
{
union { float f; unsigned u; };
f = x;
return u;
}
仅出于完整性,这是一个参考版本 foo
. 。也没有定义的行为,对吗?
unsigned ref(float x)
{
return (unsigned&)x;
}
因此,是否可以从浮子中提取钻头(假设两者都是32位, , 当然)?
编辑:这是 memcpy
Goz提出的版本。由于许多编译器不支持 static_assert
但是,我替换了 static_assert
使用一些模板元编程:
template <bool, typename T>
struct requirement;
template <typename T>
struct requirement<true, T>
{
typedef T type;
};
unsigned bits(float x)
{
requirement<sizeof(unsigned)==sizeof(float), unsigned>::type u;
memcpy(&u, &x, sizeof u);
return u;
}
解决方案
真正避免任何问题的唯一方法就是记忆。
unsigned int FloatToInt( float f )
{
static_assert( sizeof( float ) == sizeof( unsigned int ), "Sizes must match" );
unsigned int ret;
memcpy( &ret, &f, sizeof( float ) );
return ret;
}
因为您正在记住固定量,所以编译器将将其优化。
就是说,工会方法得到了非常广泛的支持。
其他提示
工会黑客肯定是不确定的行为,对吗?
是的,否。根据标准,绝对是不确定的行为。但是,这是一种常用的技巧,GCC和MSVC以及我知道的其他所有流行的编译器都明确保证它是安全的并且可以按预期工作。
以下不违反混叠规则,因为它不使用lvalues访问任何地方的不同类型
template<typename B, typename A>
B noalias_cast(A a) {
union N {
A a;
B b;
N(A a):a(a) { }
};
return N(a).b;
}
unsigned bar(float x) {
return noalias_cast<unsigned>(x);
}
如果您真的想对浮点类型的大小不知所措,而只需返回原始位,请做这样的事情:
void float_to_bytes(char *buffer, float f) {
union {
float x;
char b[sizeof(float)];
};
x = f;
memcpy(buffer, b, sizeof(float));
}
然后像这样称呼它:
float a = 12345.6789;
char buffer[sizeof(float)];
float_to_bytes(buffer, a);
当然,该技术将产生特定于机器字节订购的输出。
不隶属于 StackOverflow