这是Quake III Arena的快速反平方根实现:
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
我注意到 long int i
获取 float {{1}的地址(强制转换为long *
)的解除引用值}。然后代码在y
上执行操作,然后将解除引用的值存储在i
的地址(强制转换为float *
)到i
。
这会破坏严格的别名规则,因为y
与i
的类型不同吗?
我认为也许它没有,因为该值被解除引用并且被复制;所以操作是在副本而不是原件上进行的。
答案 0 :(得分:6)
是的,此代码严重破坏并调用未定义的行为。特别要注意这两行:
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
由于对象*(long *)&y
具有类型long
,编译器可以自由地假设它不能为类型为float
的对象设置别名;因此,编译器可以相对于彼此重新排序这两个操作。
要修复它,应该使用联合。
答案 1 :(得分:4)
是的,它打破了别名规则。
在现代C中,您可以将i = * (long *) &y;
更改为:
i = (union { float f; long l; }) {y} .l;
和y = * (float *) &i;
:
y = (union { long l; float f; }) {i} .f;
如果你保证在使用的C实现中long
和float
具有合适的大小和表示,那么行为由C标准定义:一个对象的字节type将被重新解释为另一种类型。
答案 2 :(得分:3)
是的,它打破了别名规则。
像i = * ( long * ) &y;
这样的最干净的解决方案就是:
memcpy(&i, &y, sizeof(i)); // assuming sizeof(i) == sizeof(y)
避免了对齐和别名问题。启用优化后,通常只需用少量指令替换对memcpy()
的调用。
正如任何其他方法所建议的那样,此方法不会解决与陷阱表示相关的任何问题。但是,在大多数平台上,整数中没有陷阱表示,如果您知道浮点格式,则可以避免使用浮点格式陷阱表示(如果有)。
答案 3 :(得分:1)
i = * ( long * ) &y;
这会破坏别名规则,因此会调用未定义的行为。
您正在访问类型不同于y
的对象float
,或char
的签名/无符号变体。
y = * ( float * ) &i;
上述声明也打破了别名规则。