这是从雷神之锤3的高速逆平方根实现:
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
需要在地址解除引用的值(转换为long *
) 浮动 y
。 然后,该代码执行对操作i
存储在该地址处解除引用值(转换为前float *
的) i
到y
。
这会打破严格走样规则,因为i
是不是同一类型的y
?
我想,也许它并不因为价值被取消引用和复制 ; 所以操作都在一个副本,而不是原来的执行。
Yes, this code is badly broken and invokes undefined behavior. In particular, notice these two lines:
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
Since the object *(long *)&y
has type long
, the compiler is free to assume it cannot alias an object of type float
; thus, the compiler could reorder these two operations with respect to one another.
To fix it, a union should be used.
是的,它打破别名规则。
在现代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标准定义。
是的,它打破别名规则。
最干净的修复对于像i = * ( long * ) &y;
会是这样的:
memcpy(&i, &y, sizeof(i)); // assuming sizeof(i) == sizeof(y)
它避免了对齐和走样的问题。 并启用了优化,调用memcpy()
通常应该只有少数的指令所取代。
就像任何其他方法建议,这种方法不能解决与陷阱表示任何问题。 在大多数平台上,但是,也有在整数没有陷阱表示,如果你知道你的浮点格式就可以避免浮点格式陷阱表示,如果有任何。
i = * ( long * ) &y;
This breaks aliasing rules and therefore invokes undefined behavior.
You are accessing object y
with a type different than float
, or a signed / unsigned variant of char
.
y = * ( float * ) &i;
This statement above also breaks aliasing rules.