Quake3 快速开平方和开平方倒数计算优化

 在Quake3中,卡马克使用了广为人知的魔数来优化,开平方和开平方倒数计算。而在Doom3中改为查表计算了。

   

[cpp]  view plain  copy
 
  1. /* 
  2. ================ 
  3. SquareRootFloat 
  4. ================ 
  5. */  
  6. float SquareRootFloat(float number) {  
  7.     long i;  
  8.     float x, y;  
  9.     const float f = 1.5F;  
  10.   
  11.     x = number * 0.5F;  
  12.     y  = number;  
  13.     i  = * ( long * ) &y;  
  14.     i  = 0x5f3759df - ( i >> 1 );  
  15.     y  = * ( float * ) &i;  
  16.     y  = y * ( f - ( x * y * y ) );  
  17.     y  = y * ( f - ( x * y * y ) );  
  18.     return number * y;  
  19. }  

[cpp]  view plain  copy
 
  1. /* 
  2. ** float q_rsqrt( float number ) 
  3. */  
  4. float Q_rsqrt( float number )  
  5. {  
  6.     long i;  
  7.     float x2, y;  
  8.     const float threehalfs = 1.5F;  
  9.   
  10.     x2 = number * 0.5F;  
  11.     y  = number;  
  12.     i  = * ( long * ) &y;                       // evil floating point bit level hacking  
  13.     i  = 0x5f3759df - ( i >> 1 );               // what the fuck?  
  14.     y  = * ( float * ) &i;  
  15.     y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration  
  16. //  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed  
  17.   
  18.     return y;  
  19. }  

    开平方和开平方倒数的算法是一样,都使用了魔数0x5f3759df,利用牛顿迭代法进行迭代。只是在上面的迭代中,开平方迭代了2次,开平方倒数迭代了1次。开平方最后结果,是开平方倒数在乘以原数值的结果。在GameDev.net上有人做过测试,该函数的相对误差约为0.177585%,速度比C标准库的sqrt提高超过20%。如果增加一次迭代过程,相对误差可以降低到e-004 的级数,但速度也会降到和sqrt差不多。


    下面对这2个算法进行微量的优化。

  • 利用union去除float类型的强转,和一些赋值操作
  • long类型改用int类型
  • 把中间变量的赋值操作去除,替换到计算式中
    
[cpp]  view plain  copy
 
  1. static float InvSqrtf(float x)  
  2. {  
  3.     union  
  4.     {  
  5.         float x;  
  6.         int   i;  
  7.     } u;  
  8.   
  9.     u.x = x;  
  10.     u.i = 0x5f3759df - (u.i >> 1);  
  11.   
  12.     return u.x * (1.5f - 0.5f * x * u.x * u.x);  
  13. }  
  14.   
  15. static float Sqrtf(float x)  
  16. {  
  17.     union  
  18.     {  
  19.         float x;  
  20.         int   i;  
  21.     } u;  
  22.   
  23.     u.x = x;  
  24.     u.i = 0x5f3759df - (u.i >> 1);  
  25.   
  26.     // InvSqrt(x) * x, equal Sqrt(x)  
  27.     return u.x * (1.5f - 0.5f * x * u.x * u.x) * x;  
  28. }  

    如果,精度不够可以增加迭代次数,不过也会降低性能。经过测试精度还不错。

你可能感兴趣的:(C,Math)