Taylor Series Expansion of cos x and sin x in C Pr

2019-08-12 08:06发布

问题:

I'm working on a project for our school and we are required to create a program that computes the approximation of the Taylor Expansion Series of sin x and cos x, only using <stdio.h> and without user-defined functions other than int main(), of all angles from -180 to 180 in increments of +5. the following is my code:

#include <stdio.h>
#define PI   3.141592653589
#define NUMBER_OF_TERMS    10

int
main()
{
    int cosctr, sinctr;
    double ctr, radi;
    double cosaccu, costerm, sinaccu, sinterm;

    for (ctr = -180; ctr < 185; ctr = ctr + 5) {
        radi = ctr * PI/180.0;
        cosctr = 1;
        cosaccu = 1;
        costerm = 1;
        sinctr = 2;
        sinaccu = radi;
        sinterm = radi;
        while (cosctr <= 2*NUMBER_OF_TERMS) {
            costerm = costerm*(-1)*(radi*radi)/(cosctr*(cosctr + 1));
            cosaccu = cosaccu + costerm;
            cosctr+=2;
        } do {
            sinterm = sinterm*(-1)*(radi*radi)/(sinctr*(sinctr + 1));
            sinaccu = sinaccu + sinterm;
            sinctr+=2;
        } while (sinctr <= 2*NUMBER_OF_TERMS);
        printf("%.2lf      %.12lf      %.12lf      %.12lf\n", ctr, radi, cosaccu, sinaccu);
    } return 0;
}

The code above is accurate for a 15 terms expansion approximation. however, if I change NUMBER_OF_TERMS to, for example, 5 or 10, the approximation is flawed.
Any suggestions?

Let me clarify: I need to obtain an approximation of 5 terms, 10 terms, and 15 terms. I cannot use any other library other than <stdio.h>. I cannot use any other functions outside of int main() (I apologize for the vagueness of my explanation before).
Please answer with the included corrected code.

回答1:

The key to high precession, yet simple calculation of sind(degrees) and cosd(degrees) is to reduce the range of degree to 0 to 90 first (or even 0 to 45), using the usual trigonometric adjustments with degree arrangements first.

Reductions:
angle = fmod(angle, 360) // reduce (-360..360) or use a = a - (int)(a/360)
sin(x) = -sin(-x) // reduce to [0..360)
cos(x) = cos(-x) // reduce to [0..360)
sin(x) = -sin(x-180) // reduce to [0..180)
cos(x) = -cos(x-180) // reduce to [0..180)
sin(x) = cos(90-x) // reduce to [0..90)
Further reductions:
For [45-90) use sin(x) = cos(90-x) // reduce to [0..45)

then convert to radians and use Taylor series expansion.

Example

Note: Since code is dealing with double, typically 17 digits of precision, no need to use a course PI approximation.

// #define PI   3.141592653589
#define PI   3.1415926535897932384626433832795


回答2:

I tried your code; it works fine for me, in that it does what it looks like it's designed to do. Here's a comparison between your code's output for the cosine at 5 and 10 terms and the same approximation as calculated by Mathematica. They agree up to <10^-12, i.e. your outputted precision.:

The only problem I see with your code is that, with the way you designed your loops, you're actually taking into account NUMBER_OF_TERMS + 1 terms if you count the first terms in the expansion (i.e. the constant term for the cosine, the linear term for the sine.) You start with this first term, and then your loop adds another NUMMBER_OF_TERMS terms. If that is not by design, you're actually approximating the functions with higher precision that you are expecting.



回答3:

By its very definition, a Taylor series is a summation of an infinite series of terms.

Thus, a Taylor finite expansion only is an approximation of the true result: as the number of terms increases, the accuracy of this approximation improves.

If there are enough terms, the approximation error at some point becomes unnoticeable. However, if you try lowering the number of terms, the approximation error increases and can detected.

In your case, the approximation error is below the detection threshold for NUMBER_OF_TERMS= 15, but becomes noticeable when NUMBER_OF_TERMS= 10 or less.



回答4:

The Taylor expansions of sin(x) and cos(x) takes longer to converge as x increases. But since these are periodic functions, you don't actually need to bother expanding the series for values outside the range 0-90°.

For values of x outside this range, use the following identities:

sin(x) = -sin(x+180°) = -sin(-x) = sin(180°-x)
cos(x) = -cos(x+180°) = cos(-x) = -cos(180°-x)

For example, sin(175°) = sin(5°), cos(-120°) = -cos(60°)



回答5:

i figured it out with help from another user. turns out i was calculating terms + 1, making the answer more accurate than intended. after 15 terms the changes are past the 12th decimal point, and therefore did not show on the results.

#include <stdio.h>
#define PI   3.141592653589
#define NUMBER_OF_TERMS 10 // 5 and 15 work as well

int
main()
{
    int cosctr, sinctr;
    double ctr, radi;
    double cosaccu, costerm, sinaccu, sinterm; // accu will be final answer, term will be added to accu

    for (ctr = -180; ctr < 185; ctr+=5) { // for loop; ctr initialized at -185 and added to in increments of 5 to produce degrees
        radi = ctr * PI/180.0; // calculation for radians (assigned to radi)
        cosctr = 1; // initialization for cos counter; must be included in loop to allow correct calculations of cos
        cosaccu = 1; // first term is 1
        costerm = 1; // base term, to be multiplied with termcalc formula
        sinctr = 2; // initialization for sin counter; must be included in loop to allow correct calculations of sin
        sinaccu = radi; // first term is x, or degrees in radians (radi)
        sinterm = radi; // base term for sin
     // cos calculation
     while (cosctr < 2*NUMBER_OF_TERMS-1) { // accuracy check, 2* since increments of 2; NOTE: actual values are (1, 3, 5,...)
        costerm = costerm*(-1)*(radi*radi)/(cosctr*(cosctr + 1)); // TERMCALC FORMULA; multiplying previous term with formula creates next term
        cosaccu = cosaccu + costerm; // addition of new term to previous sum; dependent on accuracy (NUMBER_OF_TERMS)
        cosctr+=2;
     } do { // sin calculation; identical to cos, albeit with substituted vars
        sinterm = sinterm*(-1)*(radi*radi)/(sinctr*(sinctr + 1));
        sinaccu = sinaccu + sinterm;
        sinctr+=2;
     } while (sinctr < 2*NUMBER_OF_TERMS-1); // accuracy check, 2* since increments of 2; NOTE: actual values are (2, 4, 6,...)
     printf("%.2lf\t%.12lf\t%.12lf\t%.12lf\n", ctr, radi, cosaccu, sinaccu); // final display; /t used for convenience
 } return 0; // finally!!!
}