将浮点转换为长指针,再转换回浮点指针
Converting float to long pointer and back to float pointer
我正在努力理解下面的代码片段
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,使用这种技巧也可以比使用内置方法更快地完成。
- 正在将指针转换为范围
- 如何理解将半精度指针转换为无符号长指针和相关的内存对齐
- C++:Lambda 函数指针转换的用例是什么?
- 如何将 int 指针转换为浮点指针
- 为什么在将 void 指针转换为整数指针时出现分段错误
- 将(N 个字节)无符号字符指针转换为浮点数和双 C++
- 我们可以在不知道其真实类型的情况下将基类指针转换为派生类指针吗?
- C++中数组大小未知的指针转换
- Antlr cpp 运行时 任何错误的指针转换?
- std::flush可以用于将对象指针转换为其封闭数组指针吗
- C 指针转换会导致内存访问冲突
- 模板类实例化中的指针转换无效
- 函数调用歧义(用户定义的转换和 Derived2Base 指针转换)
- 如何将 void(*)() 类型的指针转换为 void*
- 数组到指针转换期间的临时具体化
- 直接为浮点变量分配十六进制整数与通过指针转换分配之间的区别
- 将基类指针转换为派生的类指针
- 应对 std::字符串中的 std::<char>指针转换后的向量
- 在编译时将函数指针转换为 std::uintptr_t
- 将基类指针转换为未知派生类指针