Vil*_*ray 9 c pointers casting c99 strict-aliasing
这是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;
}
Run Code Online (Sandbox Code Playgroud)
我注意到long int i在float的地址(转换为a long *)处取消引用的值.然后代码在将解除引用的值存储到into 的地址(强制转换为a )之前执行操作. yifloat *iy
这会打破严格的别名规则,因为i它的类型不一样y吗?
我想也许它不会,因为价值被解除引用和复制 ; 因此操作是在副本而不是原件上执行的.
是的,这段代码严重破坏并调用未定义的行为.特别注意这两行:
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
Run Code Online (Sandbox Code Playgroud)
由于对象*(long *)&y具有类型long,编译器可以自由地假设它不能为类型的对象添加别名float; 因此,编译器可以相对于彼此重新排序这两个操作.
要修复它,应该使用联合.
是的,它打破了别名规则。
在现代 C 中,您可以更改i = * (long *) &y;为:
i = (union { float f; long l; }) {y} .l;
Run Code Online (Sandbox Code Playgroud)
并y = * (float *) &i;:
y = (union { long l; float f; }) {i} .f;
Run Code Online (Sandbox Code Playgroud)
只要你有,在C实现所使用的保证,long并且float具有合适的尺寸和表示,则该行为由C标准定义:一种类型的对象的将被重新解释为另一种类型的字节数。