Completing sine implementation

exercise No. 122

Transforming arguments.

Q:

We've reached an implementation offering good results for sin ( x ) if x [ - π 2 , π 2 ] . The following rules may be used to retain precision for arbitrary argument values:

  1. x , n : sin ( 2 π n + x ) = sin ( x )

    This rule of periodicity allows us to consider only the interval [ - π , π [ like e.g. sin ( 21 ) = sin ( 21 - 3 × 2 π ) sin ( 2.15 ) .

  2. x : sin ( x ) = sin ( π - x )

    This rule allows us to narrow down values from [ - π , π [ to [ - π 2 , π 2 [ like e.g. sin (2 ) = sin ( π - 2 ) sin ( 1.14 ) .

  3. x : sin ( x ) = cos ( π 2 - x )

    This rule allows us to further narrow down values from [ - π 2 , π 2 [ to [ - π 4 , π 4 [ like e.g. sin (1.1 ) = cos ( π 2 - 1.1 ) cos ( 0.471 ) . We must however implement the corresponding power series of cos ( x ) :

    Equation 4. Power series definition of cos ( x )
    cos ( x ) = 1 - x 2 2 ! + x 4 4 ! - x 6 6 ! + ... = k = 0 ( -1 ) k x 2 k ( 2 k ) !

The above rules allow for computation of arbitrary sin ( x ) values by means of power series expansion limited to the interval [ - π 4 , π 4 ] thereby gaining high precision results. Extend your current implementation by mapping arbitrary arguments to this interval appropriately.

Hint: The standard function Math.rint(double) could be helpful: It may turn e.g. 4.47 (double) to 4 (long).

A:

For convenience reasons we start defining PI within our class:

public class Math {

  private static final double PI = java.lang.Math.PI;
 ...

Now we need two steps mapping our argument:

public static double sin(double x) {
  // Step 1: Normalize x to [-PI, +PI[
  final long countTimes2PI = (long) java.lang.Math.rint(x / 2 / PI);
  if (countTimes2PI != 0) {
    x -= 2 * PI * countTimes2PI;
  }

  // Step 2: Normalize x to [-PI/2, +PI/2]
  // Since sin(x) = sin (PI - x) we continue to normalize
  // having x in [-PI/2, +PI/2]
  if (PI/2 < x) {
    x = PI - x;
  } else if (x < -PI/2) {
    x = -x - PI;
  }

  // Step 3: Continue with business as usual
  ...

This yet sows only the result from applying the first two rules. You may also view the Javadoc and the implementation of double Math.sin(double).