我正在阅读这份文件:http://software.intel.com/en-us/articles/interactive-ray-tracing
我偶然发现了这三行代码:
SIMD版本已经快了很多,但我们可以做得更好。 英特尔为SSE2指令集添加了快速1 / sqrt(x)函数。 唯一的缺点是它的精度有限。我们需要 精度,所以我们使用Newton-Rhapson改进它:
__m128 nr = _mm_rsqrt_ps( x );
__m128 muls = _mm_mul_ps( _mm_mul_ps( x, nr ), nr );
result = _mm_mul_ps( _mm_mul_ps( half, nr ), _mm_sub_ps( three, muls ) );
此代码假定存在名为“half”的__m128变量 (四次0.5f)和变量“三”(四次3.0f)。
我知道如何使用Newton Raphson来计算函数的零,我知道如何使用它来计算数字的平方根,但我只是看不出这个代码是如何执行它的。
有人可以向我解释一下吗?
答案 0 :(得分:35)
鉴于牛顿迭代,在源代码中看到这一点应该非常简单。
__m128 nr = _mm_rsqrt_ps( x ); // The initial approximation y_0
__m128 muls = _mm_mul_ps( _mm_mul_ps( x, nr ), nr ); // muls = x*nr*nr == x(y_n)^2
result = _mm_mul_ps(
_mm_sub_ps( three, muls ) // this is 3.0 - mul;
/*multiplied by */ __mm_mul_ps(half,nr) // y_0 / 2 or y_0 * 0.5
);
确切地说,此算法适用于the inverse square root。
请注意这个still doesn't give fully a fully accurate result。具有NR迭代的rsqrtps
提供近23位的准确度,而sqrtps
的24位具有正确的最后一位舍入。
如果您想truncate the result to integer,则有限的准确性是一个问题。 (int)4.99999
是4
。另外,如果使用x == 0.0
,请注意sqrt(x) ~= x * sqrt(x)
案例,因为0 * +Inf = NaN
。
答案 1 :(得分:3)
要计算a
的倒数平方根,牛顿方法应用于具有导数0=f(x)=a-x^(-2)
的等式f'(x)=2*x^(-3)
,因此迭代步骤
N(x) = x - f(x)/f'(x) = x - (a*x^3-x)/2
= x/2 * (3 - a*x^2)
与全局收敛Heron's method相比,这种无除法的方法具有有限的收敛区域,因此您需要已经很好地逼近逆平方根以获得更好的近似。