简体   繁体   中英

Is there a best way to deal with undefined behavior in bitwise conversion between floats and integers in C++14, C++17, C++20 and different compilers?

Which way in below tests is the most preferred in terms of dealing with undefined behavior, auto-vectorization (for struct of arrays) and portability (clang,gcc,msvc,icc)?

Is there another way of doing same operation?

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

For example, is memcpy ok for converting array of floats to array of integers bitwise?

  • trick1 (union): Undefined behaviour in ISO C++, unlike ISO C99.
    The C++ compilers you mentioned support it as an extension in C++.

  • trick2 ( std::memcpy ) is your best choice before C++20 : Well defined with the precondition that sizeof(int) == sizeof(float) , but not as simple as std::bit_cast . Mainstream compilers handle it efficiently, not actually doing an extra copy of anything (effectively optimizing it away), as long as the copy size is a single primitive type and writes all the bytes of the destination object.

  • trick3 (inline asm ): Non-standard; not portable (neither CPU arch nor compiler). Seriously hinders optimisation, including auto-vectorization.

  • trick4 (deref a reinterpret_cast pointer): Undefined behaviour in ISO C++, and in practice on many real compilers (notably GCC and Clang), unless you compile with gcc -fno-strict-aliasing .


I recommend C++20 std::bit_cast when applicable. It's as efficient as memcpy , and cleaner syntax:

return std::bit_cast<int>(fvar);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM