1. 程式人生 > >[轉]純C實現sqrt,cos,sin,atan2

[轉]純C實現sqrt,cos,sin,atan2

一開始的想法就是cos,sin,atan2都可以使用泰勒級數,sqrt可以使用牛頓法。

然後。。。上網找資料。。。

首先是SQRT,這位仁兄基本思路和我一樣,但是他在最後提供的這段程式碼的確很神奇。列在下面。

[cpp] view plain copy print?
  1. float Sqrt(float x)  
  2. {  
  3.     float xhalf = 0.5f*x;  
  4.     int i = *(int*)&x;  
  5.     i = 0x5f375a86 - (i >> 1);  
  6.     x = *(float*)&i;  
  7.     x = x*(1.5f - xhalf*x*x);  
  8.     x = x*(1.5f - xhalf*x*x);  
  9.     x = x*(1.5f - xhalf*x*x);  
  10.     return 1 / x;  
  11. }  

而後是COS和SIN,也是用泰勒級數,但是程式有點問題,我做了小小修正:

[cpp] view plain copy print?
  1. float Sin(float x)  
  2. {  
  3.     int sign = 1;  
  4.     int itemCnt = 4;  
  5.     int k = 0;  
  6.     float result = 0.0f;  
  7.     float tx = 0.0f;  
  8.     int factorial = 1;  
  9.     if(x < 0)  
  10.     {  
  11.         x = -x;  
  12.         sign *= -1;  
  13.     }  
  14.     while(x >= SL_2PI)  
  15.     {  
  16.         x -= SL_2PI;  
  17.     }  
  18.     if(x > SL_PI)  
  19.     {  
  20.         x -= SL_PI;  
  21.         sign *= -1;  
  22.     }  
  23.     if(x > SL_PI_DIV_2)  
  24.     {  
  25.         x = SL_PI - x;  
  26.     }  
  27.     tx = x;  
  28.     for (k = 0; k < itemCnt; k ++)  
  29.     {  
  30.         if(k%2 == 0)  
  31.         {  
  32.             result += (tx / factorial);  
  33.         }  
  34.         else
  35.         {  
  36.             result -= (tx / factorial);  
  37.         }  
  38.         tx *= (x * x);  
  39.         factorial *= (2*(k+1));  
  40.         factorial *= (2*(k+1) + 1);  
  41.     }  
  42.     if (1 == sign)  
  43.         return result;  
  44.     else
  45.         return -result;  
  46. }  

atan2有點麻煩,因為sin和cos的級數收斂很快(分母為(2n+1)!和(2n)!),而atan不一樣,分母為2n+1。WIKI上面有一個更加有效率的公式

但是感覺收斂效果仍舊一般,所以最終選擇了積分式,程式碼如下:

[cpp] view plain copy print?
  1. float Atan2(float y, float x, int infNum)  
  2. {  
  3.     int i;  
  4.     float z = y / x, sum = 0.0f,temp;  
  5.     float del = z / infNum;  
  6.     for (i = 0; i < infNum;i++)  
  7.     {  
  8.         z = i*del;  
  9.         temp = 1 / (z*z + 1) * del;  
  10.         sum += temp;  
  11.     }  
  12.     if (x>0)  
  13.     {  
  14.         return sum;  
  15.     }  
  16.     elseif (y >= 0 && x < 0)  
  17.     {  
  18.         return sum + PI;  
  19.     }  
  20.     elseif (y < 0 && x < 0)  
  21.     {  
  22.         return sum - PI;  
  23.     }  
  24.     elseif (y > 0 && x == 0)  
  25.     {  
  26.         return PI / 2;  
  27.     }  
  28.     elseif (y < 0 && x == 0)  
  29.     {  
  30.         return -1 * PI / 2;  
  31.     }  
  32.     else
  33.     {  
  34.         return 0;  
  35.     }  
  36. }  


這個方法雖然誤差已經很小了,至少不影響我計算方向場,但是應該還有更好的。至少我在stackoverflow裡面看到的是如此。比如有如此開原始碼,蘋果的。

http://blog.csdn.net/simitwsnyx/article/details/45890281