23

我试图从浮点数中提取位而不调用未定义的行为。这是我的第一次尝试:

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;
}

当然,这有依赖字节序的缺点,但我可以忍受。

union hack 绝对是未定义的行为,对吧?

unsigned uni(float x)
{
    union { float f; unsigned u; };
    f = x;
    return u;
}

为了完整起见,这里是一个参考版本foo。还有未定义的行为,对吗?

unsigned ref(float x)
{
    return (unsigned&)x;
}

那么,是否可以从浮点数中提取位(当然,假设两者都是 32 位宽)?


编辑:这是memcpyGoz 提出的版本。由于许多编译器尚不支持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;
}
4

4 回答 4

17

真正避免任何问题的唯一方法是使用 memcpy。

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;
}

因为您正在记忆固定数量,所以编译器会对其进行优化。

也就是说,联合方法得到了非常广泛的支持。

于 2010-12-01T19:45:47.207 回答
6

union hack 绝对是未定义的行为,对吧?

是和不是。根据标准,这绝对是未定义的行为。但这是一个如此常用的技巧,以至于 GCC 和 MSVC 以及据我所知,其他所有流行的编译器都明确保证它是安全的并且可以按预期工作。

于 2010-12-01T21:34:14.360 回答
5

以下不违反别名规则,因为它没有使用左值访问任何地方的不同类型

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);
}
于 2011-02-08T16:37:31.563 回答
0

如果您真的想不知道浮点类型的大小并只返回原始位,请执行以下操作:

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);

当然,这种技术会产生特定于您机器字节顺序的输出。

于 2010-12-01T20:48:25.903 回答