我见过它使用的一个地方是 Doom 3/idTech 4 Fast Inverse Square Root实现。
对于不熟悉该算法的人来说,它本质上需要将浮点数视为整数。旧的 Quake(和更早)版本的代码通过以下方式执行此操作:
float y = 2.0f;
// treat the bits of y as an integer
long i = * ( long * ) &y;
// do some stuff with i
// treat the bits of i as a float
y = * ( float * ) &i;
GitHub上的原始资源
此代码获取浮点数的地址y
,将其转换为指向 long 的指针(即,在 Quake 日中为 32 位整数),并将其取消引用为i
. 然后它做了一些令人难以置信的奇怪的小玩意儿,反之亦然。
这样做有两个缺点。一个是复杂的地址,强制转换,取消引用过程强制y
从内存中读取值,而不是从寄存器1中读取,并且在返回的路上也是如此。然而,在 Quake 时代的计算机上,浮点寄存器和整数寄存器是完全分开的,因此您几乎必须将内存推送到内存并返回以处理此限制。
第二个是,至少在 C++ 中,进行这种强制转换是非常不受欢迎的,即使在执行诸如此函数之类的相当于巫术的事情时也是如此。我确信有更多令人信服的论点,但我不确定它们是什么:)
因此,在 Doom 3 中,id 在他们的新实现中包含了以下位(它使用了一组不同的位旋转,但类似的想法):
union _flint {
dword i;
float f;
};
...
union _flint seed;
seed.i = /* look up some tables to get this */;
double r = seed.f; // <- access the bits of seed.i as a floating point number
GitHub上的原始资源
理论上,在 SSE2 机器上,这可以通过单个寄存器访问;我不确定在实践中是否有任何编译器会这样做。在我看来,它仍然比早期 Quake 版本中的铸造游戏更干净一些。
1 - 忽略“足够高级的编译器”参数