zoukankan      html  css  js  c++  java
  • cream 的qsqrt 及其原理

    首先,是creamk 的qsort:

    1. float Q_rsqrt( float number )
    2. {
    3. long i;
    4. float x2, y;
    5. const float threehalfs = 1.5F;
    6. x2 = number * 0.5F;
    7. y = number;
    8. i = * ( long * ) &y; // evil floating point bit level hacking
    9. i = 0x5f3759df - ( i >> 1 ); // what the fuck?
    10. y = * ( float * ) &i;
    11. y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
    12. // y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
    13. #ifndef Q3_VM
    14. #ifdef __linux__
    15. assert( !isnan(y) ); // bk010122 - FPE?
    16. #endif
    17. #endif
    18. return y;
    19. }

    //这段代码求解的是1.0/sqrt(x);

    以及c++中简单的实现代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    static float CarmackSqrt (float x)
    {
           float xhalf = 0.5f * x;
             
           int i = *(int*)&x;           // get bits for floating VALUE 
           i = 0x5f3759df - (i>>1);     // gives initial guess y0
           x = *(float*)&i;             // convert bits BACK to float
           x = x*(1.5f - xhalf*x*x);    // Newton step, repeating increases accuracy
           x = x*(1.5f - xhalf*x*x);    // Newton step, repeating increases accuracy
           x = x*(1.5f - xhalf*x*x);    // Newton step, repeating increases accuracy
           return (1 / x);
    }

         经过测试,这段代码是stl里的sqrt效率的4倍。辣么问题来了,为什么这段代码这么高效呢?

          首先,creamk用了求解平方根的一般方法:牛顿迭代法,其原理如下:

       
     
         设r是  的根,选取x0作为r的初始近似值,过点 做曲线  的切线L,L的方程为  ,求出L与x轴交点的横坐标  ,称x1为r的一次近似值。过点  做曲线的切线,并求该切线与x轴交点的横坐标  ,称 为r的二次近似值。重复以上过程,得r的近似值序列,其中, 称为r的 次近似值,上式称为牛顿迭代公式。
        用牛顿迭代法解非线性方程,是把非线性方程  线性化的一种近似方法。把  在点x0 的某邻域内展开成泰勒级数
     
    ,取其线性部分(即泰勒展开的前两项),并令其等于0,即
     
    ,以此作为非线性方程
      
    的近似方程
      ,则其解
     
    , 这样,得到牛顿迭代法的一个迭代关系式:
     
       已经证明,如果是连续的,并且待求的零点是孤立的,那么在零点周围存在一个区域,只要初始值位于这个邻近区域内,那么牛顿法必定收敛。 并且,如果不为0, 那么牛顿法将具有平方收敛的性能. 粗略的说,这意味着每迭代一次,牛顿法结果的有效数字将增加一倍。
      多次迭代后,数字必收敛于方程的根。
     
     

     
         第二点便是他用到了魔数0x5f3759df,使得迭代法效率大大上升,能仅用三步就算出sqrt。
        creamk寻找到这个数的过程至今令人匪夷所思,但即使是普渡大学的数学家Chris Lomont也没能找出效率比他高上多少的数字:
         普渡大学的数学家Chris Lomon在看了creamk的快速sqrt后觉得很有趣,决定要研究一下creamk弄出来的这个猜测值有什么奥秘。Lomont毕竟是一位数学家,在精心研究之后从理论上也推导出一个

              最佳猜测值,和creamk的数字非常接近, 0x5f37642f。Lomont计算出结果以后非常满意,于是拿自己计算出的起始值和creamk的神秘数字做比赛,看看谁的数字能够更快更精确的求得平方根。结果是creamk赢了。 谁也不知道creamk是怎么找到这个数字      的。

             最后Lomont发威了,采用暴力方法一个数字一个数字试过来,终于找到一个比creamk的数字效率高一些的数字,虽然实际上这两个数字所产生的结果非常近似,这个暴力得出的数字是0x5f375a86。

             Lomont为此写下一篇论文,"Fast Inverse Square Root"。

        在需要进行大数据量的sqrt运算时,creamk的qsqrt会比stl库中的 sqrt效率高出不知一星半点。

        所以当你觉得有必要用的时候,尽情的用它吧!

     
     
     
     
  • 相关阅读:
    产生财务计帐周期
    判断指定年份是否为闰年
    判断是否是闰年
    Regex quick reference
    组合外键(FOREIGN KEY)
    多列组合为主键(PRIMARY KEY)
    获取字符串开始的地址(案例)
    获取字符串结尾的电话号码(案例)
    ms sql server line feed
    如何产生连续时间?(案例)
  • 原文地址:https://www.cnblogs.com/wujiechao/p/5042572.html
Copyright © 2011-2022 走看看