在 C++14、C++17、C++20 和不同的编译器中,浮点数和整数之间的按位转换是否有处理未定义行为的最佳方法?
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?
在处理未定义行为、自动矢量化(对于数组结构)和可移植性(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;
}
例如,memcpy 可以按位将浮点数组转换为整数数组吗?
trick1(联合):ISO C++ 中的未定义行为,与 ISO C99 不同。
您提到的 C++ 编译器支持它作为 C++ 中的扩展。
trick2 (std::memcpy
) 是您在 C++20 之前的最佳选择:定义良好,前提是 sizeof(int) == sizeof(float)
,但不像std::bit_cast
那么简单。主流编译器有效地处理它,实际上并没有做任何额外的复制(有效地优化它),只要复制大小是单一原始类型并写入目标对象的所有字节。
trick3(内联asm
):Non-standard;不可移植(CPU arch 和编译器都不行)。严重阻碍优化,包括auto-vectorization.
trick4(取消引用 reinterpret_cast
指针):ISO C++ 中的未定义行为,以及许多实际编译器(尤其是 GCC 和 Clang)的实践,除非您使用 gcc -fno-strict-aliasing
.
如果适用,我推荐 C++20 std::bit_cast
。它和 memcpy
一样高效,语法更清晰:
return std::bit_cast<int>(fvar);
在处理未定义行为、自动矢量化(对于数组结构)和可移植性(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;
}
例如,memcpy 可以按位将浮点数组转换为整数数组吗?
trick1(联合):ISO C++ 中的未定义行为,与 ISO C99 不同。
您提到的 C++ 编译器支持它作为 C++ 中的扩展。trick2 (
std::memcpy
) 是您在 C++20 之前的最佳选择:定义良好,前提是sizeof(int) == sizeof(float)
,但不像std::bit_cast
那么简单。主流编译器有效地处理它,实际上并没有做任何额外的复制(有效地优化它),只要复制大小是单一原始类型并写入目标对象的所有字节。trick3(内联
asm
):Non-standard;不可移植(CPU arch 和编译器都不行)。严重阻碍优化,包括auto-vectorization.trick4(取消引用
reinterpret_cast
指针):ISO C++ 中的未定义行为,以及许多实际编译器(尤其是 GCC 和 Clang)的实践,除非您使用gcc -fno-strict-aliasing
.
如果适用,我推荐 C++20 std::bit_cast
。它和 memcpy
一样高效,语法更清晰:
return std::bit_cast<int>(fvar);