什么是一种正确的方式类型 - 将一个float到一个int,反之亦然? [英] What's a proper way of type-punning a float to an int and vice-versa?
问题描述
下面的代码通过一些bit hack执行快速的平方根运算。
该算法可能是由Silicon Graphics在20世纪90年代初开发的,它也出现在Quake 3中。
更多信息
但是,我从GCC C ++编译器获得以下警告:取消引用类型冲突指针将破坏严格别名规则
应该使用<$ c在这种情况下,是否需要改变$ c> static_cast , reinterpret_cast
或 dynamic_cast
float InverseSquareRoot(float x)
{
float xhalf = 0.5f * x;
int32_t i = *(int32_t *)& x;
i = 0x5f3759df - (i> 1);
x = *(float *)& i;
x = x *(1.5f - xhalf * x * x);
return x;
}
UPDATE
根据答案,我制作了一些C ++ 11ish伪函数,以方便应用。 b
$ b
C99版本
(但编译器支持的最多,理论上可能是某些未定义的行为)
template< typename T,typename U>
inline T pseudo_cast(const U& x)
{
union {U from; T to; } __x = {x};
return __x.to;
}
通用版本
接受的答案)
投放大小相同的类型:
#include< cstring>
template< typename T,typename U>
inline T pseudo_cast(const U& x)
{
static_assert(sizeof(T)== sizeof(U),pseudo_cast不能处理不同大小的类型);
T to;
std :: memcpy(& to,& x,sizeof(T));
return to;
}
投放任何尺寸的类型:
#include< cstring>
template< typename T,typename U>
inline T pseudo_cast(const U& x)
{
T to = T(0);
std :: memcpy(& to,& x,(sizeof(T)< sizeof(U))?sizeof(T):sizeof(U)
return to;
}
使用方式: b
$ b
float f = 3.14f;
uint32_t u = pseudo_cast< uint32_t>(f);
使用 memcpy
。
float xhalf = 0.5f * x;
uint32_t i;
assert(sizeof(x)== sizeof(i));
std :: memcpy(& i,& x,sizeof(i));
i = 0x5f375a86 - (i> 1);
std :: memcpy(& x,& i,sizeof(i));
x = x *(1.5f - xhalf * x * x);
return x;
原始代码尝试初始化 int32_t
通过首先通过 int32_t
指针访问 float
对象,这是规则被破坏的地方。 C风格的转换相当于 reinterpret_cast
,所以改为 reinterpret_cast
不会有太大的区别。 p>
使用memcpy的重要区别是,字节从 float
复制到 int32_t
,但是从不通过
int32_t
lvalue访问 float
对象,因为 memcpy
接受指向void的指针,它的内部是神奇的,不破坏别名规则。
The code below performs a fast inverse square root operation by some bit hacks. The algorithm was probably developed by Silicon Graphics in early 1990's and it's appeared in Quake 3 too. more info
However I get the following warning from GCC C++ compiler: dereferencing type-punned pointer will break strict-aliasing rules
Should I use static_cast
, reinterpret_cast
or dynamic_cast
instead in such situations?
float InverseSquareRoot(float x)
{
float xhalf = 0.5f*x;
int32_t i = *(int32_t*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}
UPDATE
Based on the answers I made some C++11ish pseudo-cast function for ease of application.
C99 version (however the most compiler support it, theoretically could be undefined behaviour in some)
template <typename T, typename U>
inline T pseudo_cast(const U &x)
{
union { U from; T to; } __x = {x};
return __x.to;
}
Universal versions (based on the accepted answer)
Cast types with the same size:
#include <cstring>
template <typename T, typename U>
inline T pseudo_cast(const U &x)
{
static_assert(sizeof(T) == sizeof(U), "pseudo_cast can't handle types with different size");
T to;
std::memcpy(&to, &x, sizeof(T));
return to;
}
Cast types with any sizes:
#include <cstring>
template <typename T, typename U>
inline T pseudo_cast(const U &x)
{
T to = T(0);
std::memcpy(&to, &x, (sizeof(T) < sizeof(U)) ? sizeof(T) : sizeof(U));
return to;
}
Use it like:
float f = 3.14f;
uint32_t u = pseudo_cast<uint32_t>(f);
Forget casts. Use memcpy
.
float xhalf = 0.5f*x;
uint32_t i;
assert(sizeof(x) == sizeof(i));
std::memcpy(&i, &x, sizeof(i));
i = 0x5f375a86 - (i>>1);
std::memcpy(&x, &i, sizeof(i));
x = x*(1.5f - xhalf*x*x);
return x;
The original code tries to initialize the int32_t
by first accessing the float
object through an int32_t
pointer, which is where the rules are broken. The C-style cast is equivalent to a reinterpret_cast
, so changing it to reinterpret_cast
would not make much difference.
The important difference when using memcpy is that the bytes are copied from the float
into the int32_t
, but the float
object is never accessed through an int32_t
lvalue, because memcpy
takes pointers to void and its insides are "magical" and don't break the aliasing rules.
这篇关于什么是一种正确的方式类型 - 将一个float到一个int,反之亦然?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!