将浮点转换为长指针,再转换回浮点指针

Converting float to long pointer and back to float pointer

本文关键字:指针 转换      更新时间:2023-10-16

我正在努力理解下面的代码片段

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,而不是先引用然后取消引用浮点。

我是指针转换的新手,所以请耐心等待。

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

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

让我们把这句话分解一下:

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

  • (long*):强制转换为类型。应用于&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(严重损坏,你实际上不得不用它强迫他们做你想做的事。

好的,所以您看到的是浮点处理器速度慢或不存在时的一些古老技巧。我怀疑原作者是否会为继续使用它辩护。它也不符合现代语言透明度的要求(即"未定义的行为"(,因此可能无法移植到所有编译器或解释器,也无法通过lint和valgrind等高质量工具正确处理,但这是80年代和90年代编写快速代码的方式。

在位级别,所有内容都存储为字节。long存储在4个字节中,float也存储在4字节中。然而,这些比特被区别对待。在integer/long中,每个比特被类似地排序为2的幂,并且可以用作比特字段。在float中,一些位用于表示应用于数字其余部分的指数。欲了解更多信息,请阅读IEEE。

这个技巧采用浮点值,并将字节视为一个整数位字段,因此它可以应用魔术。它再次将结果字节视为浮点值。

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

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