2016-02-08 28 views
1

我正在研究這個程序,它近似於泰勒級數函數。我必須對它進行近似,以使泰勒級數函數停止以0.00001的精度逼近sin函數。換句話說,最後一個近似值減去當前近似值的絕對值小於或等於0.00001。它也以15度爲增量近似從0到360度的每個角度。我的邏輯似乎是正確的,但我不明白爲什麼我得到垃圾值。任何幫助表示讚賞!如何以0.00001([C++],[Taylor Series])的精度結束while循環?

#include <math.h> 
#include <iomanip> 
#include <iostream> 
#include <string> 
#include <stdlib.h> 
#include <cmath> 
double fact(int x){ 
    int F = 1; 
    for(int i = 1; i <= x; i++){ 
     F*=i; 
    } 
    return F; 
    } 
double degreesToRadians(double angle_in_degrees){ 
    double rad = (angle_in_degrees*M_PI)/180; 
    return rad; 
} 
using namespace std; 
double mySine(double x){ 
    int current =99999; 
    double comSin=x; 
    double prev=0; 
    int counter1 = 3; 
    int counter2 = 1; 
    while(current>0.00001){ 
    prev = comSin; 
    if((counter2 % 2) == 0){ 
     comSin += (pow(x,(counter1))/(fact(counter1))); 
    }else{ 
     comSin -= (pow(x,(counter1))/(fact(counter1))); 
    } 
    current=abs(prev-comSin); 
    cout<<current<<endl; 
    counter1+=2; 
    counter2+=1; 
    } 
    return comSin; 
} 

using namespace std; 
int main(){ 
cout<<"Angle\tSine"<<endl; 
for (int i = 0; i<=360; i+=15){ 
cout<<i<<"\t"<<mySine(degreesToRadians(i)); 
} 




} 
+1

將int電流更改爲浮動電流或雙倍電流.. – Phoenix

+0

也在'fact()'中將int F更改爲雙F. – Eugene

回答

0

下面是一個例子,說明如何去做這件事。 在每次迭代中使用pow函數並計算階乘的效率非常低 - 這些通常可以作爲在每次迭代期間與總和一起更新的運行值來維護。在這種情況下,每次迭代的加數是兩個因子的乘積:x的冪和(倒數)階乘。要從一個迭代的功率因數到下一次迭代,只需乘以x * x即可。爲了從一個迭代的因子因子到下一次迭代,只需乘以((2 * n + 1)+ 1)*((2 * n + 1)+ 2),然後再遞增n(迭代次數)。

而且由於這兩個因素是乘法更新的,所以它們不需要作爲單獨的運行值存在,它們可以作爲單個運行產品存在。這也有助於避免精度問題 - 功率因數和因子可能會很快變大,但它們的比值相對逐漸變爲零,並且作爲運行值良好表現。

所以本實施例中保持這些運行值,在每次迭代時更新:

  • 「總和」
  • 「刺」,比值(當然):POW(X,2N + 1)/階乘2n + 1個
  • 「TNP1」,2 *值n + 1(在階乘更新中使用)

的運行更新值,「刺」,以便於在所述因子否定每次迭代(-1)^ N。

我還加入了函數「XlatedSine」。當x離零太遠時,總和需要更多的迭代才能獲得精確的結果,這需要更長的時間才能運行,並且可能需要比浮點值更高的精度。當x的大小超過PI時,「XlatedSine」找到另一個x,接近於零,具有sin(x)的等價值,然後在調用MaclaurinSine時使用此移位的x。

#include <iostream> 
#include <iomanip> 

// Importing cmath seemed wrong LOL, so define Abs and PI 
static double Abs(double x) { return x < 0 ? -x : x; } 
const double PI = 3.14159265358979323846; 

// Taylor series about x==0 for sin(x): 
// 
// Sum(n=[0...oo]) { ((-1)^n) * (x^(2*n+1))/(2*n + 1)! } 
// 

double MaclaurinSine(double x) { 
    const double xsq = x*x;  // cached constant x squared 
    int tnp1  = 3;   // 2*n+1      | n==1 
    double prod  = xsq*x/6; // pow(x, 2*n+1)/(2*n+1)! | n==1 
    double sum  = x;   // sum after n==0 

    for(;;) { 
     prod = -prod; 
     sum += prod; 

     static const double MinUpdate = 0.00001; // try zero -- the factorial will always dominate the power of x, eventually 

     if(Abs(prod) <= MinUpdate) { 
      return sum; 
     } 

     // Update the two factors in prod 
     prod *= xsq;      // add 2 to the power factor's exponent 
     prod /= (tnp1 + 1) * (tnp1 + 2); // update the factorial factor by two iterations 
     tnp1 += 2; 
    } 
} 

// XlatedSine translates x to an angle close to zero which will produce the equivalent result. 
double XlatedSine(double x) { 
    if(Abs(x) >= PI) { 
     // Use int casting to do an fmod PI (but symmetric about zero). 
     // Keep in mind that a really big x could overflow the int, 
     // however such a large double value will have lost so much precision 
     // at a sub-PI-sized scale that doing this in a legit fashion 
     // would also disappoint. 
     const int p = static_cast<int>(x/PI); 
     x -= PI * p; 
     if(p % 2) { 
      x = -x; 
     } 
    } 
    return MaclaurinSine(x); 
} 

double DegreesToRadians(double angle_deg) { 
    return PI/180 * angle_deg; 
} 

int main() { 
    std::cout<<"Angle\tSine\n" << std::setprecision(12); 
    for(int i = 0; i<=360; i+=15) { 
     std::cout << i << "\t" << MaclaurinSine(DegreesToRadians(i)) << "\n"; 
     //std::cout << i << "\t" << XlatedSine(DegreesToRadians(i)) << "\n"; 
    } 
}