首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >快速逆任意幂根算法的实现

快速逆任意幂根算法的实现
EN

Stack Overflow用户
提问于 2019-03-04 19:59:38
回答 1查看 666关注 0票数 3

许多资料表明,著名的快速逆平方根算法可以推广到任意幂逆根的计算.不幸的是,我还没有找到这样的C++实现,而且我自己也不擅长推广这种方法。你能帮我做这件事或者提供一个现成的解决方案吗?我认为这对许多人都是有用的,特别是有了很好的解释。

这是最初的算法,我不太明白我需要更改什么才能得到例如1 /cbrt(x)

代码语言:javascript
复制
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 );               // what the...? 
    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;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-04 20:33:04

该算法由两个步骤组成:一个粗略的解估计和几个牛顿法步骤的改进。

粗略估计

其基本思想是利用浮点数对数log2(x)与其整数表示Ix之间的关系。

(图片来源于根部)

现在,使用著名的对数身份作为根:

结合前面得到的恒等式,我们得到:

替换数值L * (B - s) = 0x3F7A3BEA,所以

Iy = 0x3F7A3BEA / c * (c + 1) - Ix / c;

对于一个简单的浮点数表示为整数和后退,使用union类型是很方便的:

代码语言:javascript
复制
   union 
   { 
      float f; // float representation
      uint32_t i; // integer representation
   } t;

   t.f = x;
   t.i = 0x3F7A3BEA / n * (n + 1) - t.i / n; // Work with integer representation
   float y = t.f; // back to float representation

请注意,对于n=2,表达式被简化为与原始i = 0x5f3759df - ( i >> 1 );相同的t.i = 0x5f3759df - t.i / 2;

牛顿解改进

变换相等

变成一个应该解决的方程:

现在构造牛顿步骤:

从编程上看,它看起来是:y = y * (1 + n - x * pow(y,n)) / n;。作为初始y,我们使用在粗糙估计步骤中得到的值。

注对于平方根(n = 2)的特殊情况,我们得到了与原公式y = y * (threehalfs - (x2 * y * y))相同的y = y * (3 - x*y*y) / 2;

最后的代码作为模板函数。参数N确定根功率。

代码语言:javascript
复制
template<unsigned N>
float power(float x) {
   if (N % 2 == 0) return power<N / 2>(x * x);
   else if (N % 3 == 0) return power<N / 3>(x * x * x);
   return power<N - 1>(x) * x;
};

template<>
float power<0>(float x){ return 1; }

// fast_inv_nth_root<2>(x) - inverse square root 1/sqrt(x)
// fast_inv_nth_root<3>(x) - inverse cube root 1/cbrt(x)

template <unsigned n>
float fast_inv_nth_root(float x)
{
   union { float f; uint32_t i; } t = { x };

   // Approximate solution
   t.i = 0x3F7A3BEA / n * (n + 1) - t.i / n;
   float y = t.f;

   // Newton's steps. Copy for more accuracy.
   y = y * (n + 1 - x * power<n>(y)) / n;
   y = y * (n + 1 - x * power<n>(y)) / n;
   return y;
}

测试

测试代码:

代码语言:javascript
复制
int main()
{
   std::cout << "|x          ""|fast2      "" actual2    "
      "|fast3      "" actual3    "
      "|fast4      "" actual4    "
      "|fast5      "" actual5    ""|" << std::endl;

   for (float i = 0.00001; i < 10000; i *= 10)
      std::cout << std::setprecision(5) << std::fixed
      << std::scientific << '|'
      << i << '|'
      << fast_inv_nth_root<2>(i) << " " << 1 / sqrt(i) << "|"
      << fast_inv_nth_root<3>(i) << " " << 1 / cbrt(i) << "|"
      << fast_inv_nth_root<4>(i) << " " << pow(i, -0.25) << "|"
      << fast_inv_nth_root<5>(i) << " " << pow(i, -0.2) << "|"
      << std::endl;
}

结果:

代码语言:javascript
复制
|x          |fast2       actual2    |fast3       actual3    |fast4       actual4    |fast5       actual5    |
|1.00000e-05|3.16226e+02 3.16228e+02|4.64152e+01 4.64159e+01|1.77828e+01 1.77828e+01|9.99985e+00 1.00000e+01|
|1.00000e-04|9.99996e+01 1.00000e+02|2.15441e+01 2.15443e+01|9.99991e+00 1.00000e+01|6.30949e+00 6.30957e+00|
|1.00000e-03|3.16227e+01 3.16228e+01|1.00000e+01 1.00000e+01|5.62339e+00 5.62341e+00|3.98103e+00 3.98107e+00|
|1.00000e-02|9.99995e+00 1.00000e+01|4.64159e+00 4.64159e+00|3.16225e+00 3.16228e+00|2.51185e+00 2.51189e+00|
|1.00000e-01|3.16227e+00 3.16228e+00|2.15443e+00 2.15443e+00|1.77828e+00 1.77828e+00|1.58487e+00 1.58489e+00|
|1.00000e+00|9.99996e-01 1.00000e+00|9.99994e-01 1.00000e+00|9.99991e-01 1.00000e+00|9.99987e-01 1.00000e+00|
|1.00000e+01|3.16226e-01 3.16228e-01|4.64159e-01 4.64159e-01|5.62341e-01 5.62341e-01|6.30948e-01 6.30957e-01|
|1.00000e+02|9.99997e-02 1.00000e-01|2.15443e-01 2.15443e-01|3.16223e-01 3.16228e-01|3.98102e-01 3.98107e-01|
|1.00000e+03|3.16226e-02 3.16228e-02|1.00000e-01 1.00000e-01|1.77827e-01 1.77828e-01|2.51185e-01 2.51189e-01|
|1.00000e+04|9.99996e-03 1.00000e-02|4.64155e-02 4.64159e-02|9.99995e-02 1.00000e-01|1.58487e-01 1.58489e-01|
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54990666

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档