浮点和严格别名

fre*_*low 22 c++ floating-point bits strict-aliasing type-punning

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

unsigned foo(float x)
{
    unsigned* u = (unsigned*)&x;
    return *u;
}
Run Code Online (Sandbox Code Playgroud)

据我了解,由于严格的别名规则,这不能保证工作,对吧?如果使用字符指针进行中间步骤,它是否有效?

unsigned bar(float x)
{
    char* c = (char*)&x;
    unsigned* u = (unsigned*)c;
    return *u;
}
Run Code Online (Sandbox Code Playgroud)

或者我是否必须自己提取单个字节?

unsigned baz(float x)
{
    unsigned char* c = (unsigned char*)&x;
    return c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
}
Run Code Online (Sandbox Code Playgroud)

当然,这有一个缺点,取决于字节顺序,但我可以忍受.

工会黑客肯定是未定义的行为,对吧?

unsigned uni(float x)
{
    union { float f; unsigned u; };
    f = x;
    return u;
}
Run Code Online (Sandbox Code Playgroud)

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

unsigned ref(float x)
{
    return (unsigned&)x;
}
Run Code Online (Sandbox Code Playgroud)

那么,是否可以从浮点数中提取位(当然,假设两者都是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;
}
Run Code Online (Sandbox Code Playgroud)

Goz*_*Goz 16

关于真正避免任何问题的唯一方法是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;
}
Run Code Online (Sandbox Code Playgroud)

因为您正在记忆固定数量,所以编译器会优化它.

这说联合方法得到了非常广泛的支持.

  • @Crashworks,呵呵呵.就个人而言,我使用memcpy技巧.你正在对别人做的事情非常明显:) (2认同)
  • @Goz:根据POSIX(http://pubs.opengroup.org/onlinepubs/9699919799/functions/memcpy.html)和ISO C标准,它是无效的*.如何在内部解释数据留给实现.gcc将memcpys转换为循环,每次转移一个基本机器单元,然后使用较短的加载/存储来转换其余部分. (2认同)

jal*_*alf 6

工会黑客肯定是未定义的行为,对吧?

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

  • 只是这是不允许的.一次只有一个工会成员"活跃".如果您写入结构的成员,那么您*只允许*从同一成员中读取.读取任何其他成员的结果未定义. (4认同)
  • @EboMike"除了"......这正是UB.从没有与联合的活动成员兼容的别名的成员读取是一种别名冲突.以下是好的例子:`union A {int a; unsigned char b; }; A x = {10}; return xb;`,因为你被允许通过`unsigned char`类型的左值访问`int`. (2认同)

Joh*_*itb 5

以下内容不违反别名规则,因为它没有使用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);
}
Run Code Online (Sandbox Code Playgroud)

  • @Johannes:这个推理仍然是正确的吗?访问"b"正在访问联盟的非活动成员. (2认同)