1

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

wikipedia image

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!

Community
  • 1
  • 1
Jonathan
  • 71
  • 1
  • 9
  • 3
    **B-b-b-b-b-b-b-BUFFER OVERFLOW!!!1!** (Okay, technically an integer overflow, but that doesn't sound as fun to wrestl-ize) –  Jan 29 '15 at 17:28
  • 1
    Try using `BigInteger`s. –  Jan 29 '15 at 17:28
  • so double is not enough? But is that the reason why the result sometimes is 4.106? – Jonathan Jan 29 '15 at 17:37
  • Factorials grow fast. Really fast. Faster than the "Well, that escalated quickly!" meme from The Anchorman. You'll need to use `BigInteger`s and `BigDecimal`s (or be careful in how you construct your terms) in order to avoid overflowing. –  Jan 29 '15 at 17:43
  • 1
    @JackManey: The real issue seems to be that x is too big for the Taylor formula to have good convergence. – Kris Jan 29 '15 at 18:42
  • 2
    Why is your `sine` method taking an `int`? – rgettman Jan 29 '15 at 18:43
  • Keep in mind, Taylor series expansion works on radians, not degrees, so if your passing degrees rather than radians, you're not going to get the results you're expecting. Try converting to radians first. – andand Jan 29 '15 at 20:12
  • 1
    It may be profitable to evaluate this polynomial in Horner form instead of relying on `Math.pow` and your `fact`. – tmyklebu Jan 29 '15 at 21:34

3 Answers3

3

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.

Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51
1

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.

Kris
  • 1,388
  • 6
  • 12