de_dust
de_dust

Reputation: 291

Increasing cosine accuracy

Given this method to compute the cosine of x:

public static double myCos(double x){
        double alteSumme, neueSumme, summand;
        int j = 0;
        neueSumme = 1.0;
        summand = 1.0;
        do
        {
            j++;
            summand *= -x * x / j;
            j++;
            summand /= j;
            alteSumme = neueSumme;
            neueSumme += summand;
        } while ( neueSumme != alteSumme );
        return alteSumme;
    }

I get inaccurate results for large x: enter image description here

I have to transform the domain from the real numbers to the interval [0,PI/2] for accurate results.

So, I wrote this method:

public static double transform(double x){
        x = x%(2*Math.PI);
        if(!(0<=x&&x<Math.PI)){
            x = -(x+Math.PI);
        }
        if(!(0<=x&&x<Math.PI/2)){
            x = -(x-2*Math.abs(x-Math.PI/2));
        }
        return x;
    }

It should work in three steps.

  1. Transform the domain from the reals to [0,2PI).

  2. Transform the domain from [0,2PI) to [0,PI).

  3. Transform the domain from [0,PI) to [0,PI/2]

But somehow it yields wrong results.

Can you help me find my mistake?

EDIT

public class Main {

    public static double transform(double x) {
        x = x % (2 * Math.PI);
        if (!(0 <= x && x < Math.PI)) {
            x = -(x + Math.PI);
        }
        if (!(0 <= x && x < Math.PI / 2)) {
            x = -(x - 2 * Math.abs(x - Math.PI / 2));
        }
        return x;
    }

    public static double myCos(double x) {

        x = transform(x);

        double alteSumme, neueSumme, summand;
        int j = 0;
        neueSumme = 1.0;
        summand = 1.0;
        do {
            j++;
            summand *= -x * x / j;
            j++;
            summand /= j;
            alteSumme = neueSumme;
            neueSumme += summand;
        } while (neueSumme != alteSumme);
        return alteSumme;
    }

    public static void main(String[] args) {
        int n = 0;
        int k =50;
        for (double y = k * Math.PI; y <= (k + 2) * Math.PI; y += Math.PI/4) {

            System.out.println(n + ":" + y + ": " + myCos(y) + " " + Math.cos(y));
            n++;
        }
    }
}

enter image description here

Upvotes: 0

Views: 166

Answers (1)

Kenney
Kenney

Reputation: 9093

UPDATE Here's an implementation of transform suited for cos only.

/**
 * @param x any value
 * @return x within [0..2PI)
public static double transform(double x) {
    x = Math.abs(x);   // We can do this because Cosine is symmetric around the y axis.
    double y = Math.floor(x / (Math.PI * 2));
    return x - y * Math.PI * 2;
}

As requested, here's my test class:

package cosine;

public class Main {

    public static double transform(double x) {
        x = Math.abs(x);
        double y = Math.floor(x / (Math.PI * 2));
        return x - y * Math.PI * 2;
    }

    public static double myCos(double x) {

        x = transform(x);

        double alteSumme, neueSumme, summand;
        int j = 0;
        neueSumme = 1.0;
        summand = 1.0;
        do {
            j++;
            summand *= -x * x / j;
            j++;
            summand /= j;
            alteSumme = neueSumme;
            neueSumme += summand;
        } while (neueSumme != alteSumme);
        return alteSumme;
    }

    public static void main(String[] args) {
        int n = 0;
        for (double y = -20 * Math.PI; y < 20 * Math.PI; y += Math.PI / 3) {
            double x = Math.PI / 3 + y;
            double tmpa, tmpb;
            System.out.println(
                    n + ":" + x + ": " + (tmpa = myCos(x)) + " " + (tmpb = Math.cos(x)) + "  DIFF: " + (tmpa - tmpb));
            n++;
        }
    }
}

UPDATE

Here's a slight improvement by only calculating the cosine over 0..π:

public static double myCos(double x) {
    // Cosine is symmetric around the Y axis: get rid of the sign.
    x = Math.abs(x);

    // Calculate the number of times 2*PI fits in x
    double y = Math.floor(x / (Math.PI * 2));

    // and subtract that many 2*PI
    x -= y * Math.PI * 2;
    // x is now within 0 and 2*PI.

    // The PI..2PI range is the negated version of 0..PI.
    double sign = 1;
    if ( x > Math.PI ) {
       sign = -1;
       // mirror x in the line x=Math.PI:
       x =  - x + Math.PI; // or: Math.PI * 2 - x
    }

    /* cosine approximation ... */

    return alteSumme * sign;
}

Upvotes: 1

Related Questions