在 C++14、C++17、C++20 和不同编译器中,是否有最佳方法来处理浮点数和整数之间的按位转换中的未定义行为?

hus*_*sik 2 c++ simd type-punning c++17 c++20

在处理未定义行为、自动向量化(对于数组结构)和可移植性(clang、gcc、msvc、icc)方面,以下测试中哪种方式是最优选的?

还有另一种方法可以进行相同的操作吗?

#include <iostream>
#include <cstring>

union trick1
{
  float fvar;
  int ivar;
};

struct trick2
{
  float fvar;
  int ivar()
  {
      int result;
      std::memcpy(&result,&fvar,sizeof(float));
      return result;
  }
};

struct trick3
{
    float fvar;
    int ivar()
    {
        int result=0;
        asm ("mov %0,%0"
         : "=r" (result)
         : "0" (fvar));
        return result;
    }
     
};

struct trick4
{
    float fvar;
    int ivar()
    {
        int result;
        result = *reinterpret_cast<int*>(&fvar);
        return result;
    }
};

int main()
{
    trick1 test1;
    test1.fvar = 3.14f;
    // 1078523331
    std::cout<<test1.ivar<<std::endl;

    trick2 test2;
    test2.fvar = 3.14f;
    // 1078523331
    std::cout<<test2.ivar()<<std::endl;
    
    trick3 test3;
    test3.fvar = 3.14f;
    // 1078523331
    std::cout<<test3.ivar()<<std::endl;  
    
    trick4 test4;
    test4.fvar = 3.14f;
    // 1078523331
    std::cout<<test4.ivar()<<std::endl;  
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

例如,memcpy 可以将浮点数数组按位转换为整数数组吗?

eer*_*ika 9

  • rick1(联合):ISO C++ 中未定义的行为,与 ISO C99 不同。
    您提到的 C++ 编译器支持将其作为 C++ 的扩展。

  • trick2( std::memcpy) 是 C++20 之前的最佳选择:定义良好,前提是sizeof(int) == sizeof(float),但不像 那样简单std::bit_cast。只要副本大小是单个基元类型并写入目标对象的所有字节,主流编译器就可以有效地处理它,实际上不会执行任何额外的副本(有效地优化它)。

  • tricks3(内联asm):非标准;不可移植(无论是CPU架构还是编译器)。严重阻碍优化,包括自动矢量化。

  • tricks4(derefreinterpret_cast指针):ISO C++ 中的未定义行为,以及许多实际编译器(特别是 GCC 和 Clang)的实践,除非您使用gcc -fno-strict-aliasing.


std::bit_cast如果适用,我推荐 C++20 。它与 一样高效memcpy,并且语法更清晰:

return std::bit_cast<int>(fvar);
Run Code Online (Sandbox Code Playgroud)

  • 对于定义联合类型双关行为的编译器(大多数但不是全部实际的 C++ 编译器)或重新解释强制转换(仅 MSVC 和 ICC,或“gcc -fno-strict-aliasing”),这些与 memcpy 一样好。 (2认同)
  • @huseyintugrulbuyukisik:是的,这最不可能损害使用它的算法的自动矢量化。顺便说一句,您不希望这个单元素 memcpy 被“矢量化”,您希望它完全优化,例如,如果您正在执行旧版 bithack fastinvsqrt 而不是使用 XMM/YMM/ZMM 寄存器中的 SIMD 整数移位一个“rsqrtps”内在函数。 (2认同)