9

这是来自 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在 float的地址(转换为 a )i处获取取消引用的值。然后,代码在将取消引用的值存储在into的地址(转换为 a )之前执行操作。long * yifloat *iy

这是否会破坏严格的别名规则,因为i与 不是同一类型y

我认为也许它不会因为值被取消引用和复制;所以操作是在副本而不是原件上执行的。

4

4 回答 4

6

是的,这段代码被严重破坏并调用了未定义的行为。特别要注意这两行:

    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking

由于对象*(long *)&y具有类型long,编译器可以自由地假设它不能为类型对象设置别名float;因此,编译器可以相互重新排序这两个操作。

要修复它,应该使用联合。

于 2013-04-04T18:40:05.617 回答
5

是的,它打破了别名规则。

在现代 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 标准定义:一种类型的对象的字节将被重新解释为另一种类型。

于 2013-04-04T19:11:23.543 回答
3

是的,它打破了别名规则。

最干净的解决方法i = * ( long * ) &y;是:

  memcpy(&i, &y, sizeof(i)); // assuming sizeof(i) == sizeof(y)

它避免了对齐和混叠问题。memcpy()在启用优化的情况下,通常应该只用几条指令替换对的调用。

就像建议的任何其他方法一样,这种方法不能解决与陷阱表示相关的任何问题。然而,在大多数平台上,整数中没有陷阱表示,如果您知道浮点格式,则可以避免浮点格式陷阱表示(如果有的话)。

于 2013-04-04T21:52:55.630 回答
1

i = * ( long * ) &y;

这打破了别名规则,因此调用了未定义的行为。

您正在访问y类型不同于 的对象float,或者是 的有符号/无符号变体char

y = * ( float * ) &i;

上面的这个语句也打破了别名规则。

于 2013-04-04T18:39:46.123 回答