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!
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.
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.
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.