0

我正在尝试理解从此处获取的以下代码片段

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 );               // ??? 
    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;
}

我不明白的是从浮点到长指针再回到浮点指针的转换。为什么我们不能简单地做i=y而不是先引用然后取消引用浮点数。

我是指针转换的新手,所以请多多包涵。

4

2 回答 2

3

这段代码被剪断显然是快速的平方根倒数。那里的指针语义并没有真正用于做指针的事情,而是将某个内存位置的位重新解释为不同的类型。

如果您要分配i=y这将变成从浮点到整数的截断转换。然而,这不是这里想要的。您真正想要的是对位的原始访问,这在浮点类型变量上并不简单。

让我们分解一下这个陈述:

i  = * ( long * ) &y;
  • &y: y 的地址。这个表达式的类型是(float*)

  • (long*): 转换为类型。Appled&y压倒了信息,即这是浮点类型对象的地址。

  • *: 取消引用,这意味着“读出”位于给定地址的任何内容,并解释为被取消引用的指针的基本类型。我们已经覆盖了它,(long*)并且本质上是在对编译器撒谎。

出于所有意图和目的,这会破坏指针别名规则并调用未定义的行为。你不应该这样做(注意事项¹)。

进行这种欺骗的某种定义明确的方式(至少它不会破坏指针别名规则)是通过union.

float Q_rsqrt( float number )
{
    union {
        float y;
        long  i;
    } fl;
    float x2;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    fl.y  = number;
    fl.i  = 0x5f3759df - ( fl.i >> 1 );                   // ??? 
    fl.y  = fl.y * ( threehalfs - ( x2 * fl.y * fl.y ) ); // 1st iteration
//  fl.y  = fl.y * ( threehalfs - ( x2 * fl.y * fl.y ) ); // 2nd iteration, this can be removed

    return fl.y;
}

编辑:

应该注意的是,上面说明的通过联合的类型双关语也不受 C 语言标准的认可。然而,与语言未定义的行为不同,到目前为止,该标准将以此方式完成的联合访问的细节保留为实现相关的行为。由于类型双关是某些任务所必需的,我认为已经提出了一些建议,以便在即将到来的 C 编程语言标准中很好地定义这一点。

出于所有意图和目的,实际上所有编译器都支持上述方案,而如果启用所有优化路径,则通过指针强制转换的类型双关将导致奇怪的事情发生。


1:一些编译器(旧的或自定义编写的,用于特定的语言扩展——我在看你的 CUDA nvcc)被严重破坏,你实际上必须强迫他们做你想做的事。

于 2018-07-19T09:24:57.827 回答
1

好的,所以您正在研究从浮点处理器运行缓慢或不存在时开始的一些古老黑客技术。我怀疑原作者会捍卫继续使用它。它也不符合现代语言透明度要求(即“未定义行为”),因此可能无法移植到所有编译器或解释器,或者无法由 lint 和 valgrind 等质量工具正确处理,但它就是这样快速代码是在 80 年代和 90 年代编写的。

在位级别,所有内容都存储为字节。long 存储在 4 个字节中,float 也存储在 4 个字节中。然而,这些位的处理方式非常不同。在整数/长整数中,每个位的排列方式与 2 的幂类似,并且可以用作位字段。在浮点数中,一些位用于表示应用于数字其余部分的指数。有关更多信息,请阅读 IEEE。

这个技巧采用浮点值,并将字节视为整数位字段,因此它可以应用魔法。它查看结果字节,就好像它们再次是浮点数一样。

我不知道那个魔法到底是什么。没有其他人这样做,甚至可能不是写它的人,因为它没有被评论。另一方面,厄运和地震的来源确实曾经是邪教代码阅读,所以也许有人记得细节?

在“过去的美好时光”中曾经有很多这样的技巧,但现在它们相对没有必要,因为浮点现在内置在主处理器中,并且与整数运算一样快,有时甚至更快。最初,即使是从协处理器上传和下载小的整数也可以比使用内置方法更快地完成。

于 2018-07-19T09:31:42.970 回答