"improve" 单位长度向量的长度的快速方法

Fast way to "improve" the length of a unit length vector

当已知向量已经几乎是单位长度时,在性能关键代码中支付完整的向量规范化似乎是浪费。

有谁知道让双精度 3D 向量的长度更接近 1 的快速、实用的方法吗?我正在想象一种基于 Newton-Raphson 迭代或 1 左右有限泰勒展开的迭代方法。

Here 是一种实际情况,在这种情况下,这样的例程可能会有用。 incoming 向量已经接近单位长度,但如果没有明确的规范化,它仍然会触发断言。

可以使用 SSE 2、SSE 4.2 或 AVX 内在函数。

手头的问题归结为找到(近似值)reciprocal square root

SSE 和 AVX 包括一个近似倒数平方根机器指令,rsqrt,它特别适合这个。根据原文AMD64 Architecture Programmer's Manual, volume 1,平方根倒数变体的最大相对误差最多为1.5×2-12,即小于0.0004

如果使用GCC,可以使用__builtin_ia32_rsqrtss()SSE built-in函数计算向量长度平方的倒数平方根,并将向量分量乘以结果,得到得到一个 "almost unit" 向量。

请注意,SSE 和 AVX 都提供了加速平方长度计算以及乘以每个分量的函数。 (不过,您需要将比例因子复制到大小相等的向量。)


没有SSE/AVX,一般的问题是我们希望将向量分量乘以 f(S) ≃ sqrt(1/S ) == 1/sqrt(S),其中S是向量与自身的内积(点积) ,即其长度的平方;但是 sqrt() 被认为太慢了,并且已知 S 已经接近 1.

任意函数f(S),其值在1和sqrt(1/S)之间,在我们考虑的范围内"close to 1",将工作。我能想到的最简单的函数形式是 f(S) = (C + 1 - S) / C。对于 S = 0.52 到 22 (即对于长度在 1/2 和 2 之间的向量), C 是 6.

如果我们没有任何对平方根倒数的硬件支持,我将尝试的第一个近似值将遵循以下几行:

  1. 计算向量的平方长度S

  2. 计算 M = 0.125 * (9 - S)

    注意任何常量对 C1C2 = 1 + 1 / C1 应该有效,只是范围和收敛速度不同。我选择 C1 = 1/8 只是因为它在 IEEE-754 floating-point 表示中是精确的,通常是乘法比除法快得多。其他值(如我上面提到的范围为 0.5 到 2 的 1/6)不精确,可能需要手动调整(以一种或另一种方式调整两个常量中的最低有效单位)。

  3. 将向量的每个分量乘以 M

如果这没有产生足够好的结果,我会停止担心它,而是使用(硬件)平方根。 (在某些架构上,将平方长度转换为单精度以计算比例因子可以产生显着的加速。但是在 x86/AMD64 上则不然。)