Sine calculation with Taylor series not working

2019-01-29 05:40发布

问题:

I wrote a sine function in Java, that should calculate the sine of x like

My function looks like

public static double sine(int x) {
    double result = 0;
    for(int n = 0; n <= 8; n++) {
        double temp = 2 * n + 1;
        result += Math.pow(-1, n) * Math.pow(x, temp) / fact(temp); // fact() calculates faculty

    }
    return result;

}

Only a few decimal places match to Math.sin(), but only if x is smaller then 6 and the limit of n equals to 8.
Sometimes, if x is greater than 6 sine() returns even 4.106 or if limit of n is greater than 32, it returns NaN or -Infinity...
What am I doing wrong?

Hope you can help me and thanks in advance!

回答1:

With such a low value of n, the Taylor series is a poor approximation of sine for high numbers. For degree 8, any angle higher than around 4 will produce a significantly inaccurate result.

Because sine is periodic, one easy solution would be to modulus divide the angle (in radians) by 2*pi. The StrictMath Class in Java reduces the angle in a different way but the idea is the same. In that case, they reduce the angle to between [-pi/4, pi/4] to improve accuracy. This tool demonstrates how accuracy improves as the degree of the Taylor series increases.



回答2:

Taking a step back, the astonishing fact is that the sine series evaluates to a bounded function in the first place. After all, it is a power series closely related to the exponential function. That the polynomials that occur as partial sums have large values for large arguments is a general property of polynomials and thus not astonishing at all.


Avoiding the recomputation of recursive functions like powers of the same argument and factorials is always a good idea. Thus a reduction to (a minimal amount of) elementary arithmetic operations looks like this:

public static double sine(int terms, double x) {
    double result = 1;
    double mxx = -x*x;
    double addens = 1;
    double temp = 2;
    for(int n = 2; n <= terms; n++) {
        addens *= mxx/temp++/temp++;
        result += addens; 
    }
    return x*result;

}

Note that the type of x is now double.


Another breaking condition for the loop would be

   while(1+addens != 1)

to use a flexible number of terms until the contribution of addens becomes negligible. This gives accurate results for a larger range of arguments, however for large arguments the cost will dramatically increase.

Then one can explore a halving-and-squaring strategy with a simultaneous computation of sin and cos.



回答3:

As monolyth421 already mentioned, your problem is that the Taylor expansion works good for small (close to zero) values of x. If you want to obtain a good accuracy, you should consider reducing the argument to the interval [-pi/2 , pi/2] (using trigonometric identities) and then use the Taylor expansion. Only a few terms should be then enough to get good accuracy.