Fast sine and cosine function in java

☆樱花仙子☆ 提交于 2019-12-07 11:43:27

问题


I know about the Math.sin() and Math.cos() functions, but I'm wondering if there's a way I can create (or use an already-existing) a faster function, given that I don't care about pinpoint accuracy. I'm looking to execute a basic sin or cos calculation, and have it perform essentially as fast as possible. Would simply iterating the sigma a few times be any faster than Math.sin()?


回答1:


Since you don't care much about accuracy store it in a table that is precomputed or only computed once, this is what I do when I want to avoid calls to Math which can be expensive when done alot.

Roughly

public class CosSineTable {
double[] cos = new double[361];
double[] sin = new double[361];
private static CosSineTable table = new CosSineTable();

private CosSineTable() {
    for (int i = 0; i <= 360; i++) {
        cos[i] = Math.cos(Math.toRadians(i));
        sin[i] = Math.sin(Math.toRadians(i));
    }
}

public double getSine(int angle) {
    int angleCircle = angle % 360;
    return sin[angleCircle];
}

public double getCos(int angle) {
    int angleCircle = angle % 360;
    return cos[angleCircle];
}

public static CosSineTable getTable() {
    return table;
}
}

I leave the optimization of the loop and methods to you.




回答2:


A pre-calculated table's the way to go. Here's an implementation:

static final int precision = 100; // gradations per degree, adjust to suit

static final int modulus = 360*precision;
static final float[] sin = new float[modulus]; // lookup table
static { 
    // a static initializer fills the table
    // in this implementation, units are in degrees
    for (int i = 0; i<sin.length; i++) {
        sin[i]=(float)Math.sin((i*Math.PI)/(precision*180));
    }
}
// Private function for table lookup
private static float sinLookup(int a) {
    return a>=0 ? sin[a%(modulus)] : -sin[-a%(modulus)];
}

// These are your working functions:
public static float sin(float a) {
    return sinLookup((int)(a * precision + 0.5f));
}
public static float cos(float a) {
    return sinLookup((int)((a+90f) * precision + 0.5f));
}

On my laptop, these were about 6x faster than Math.sin.

I only used one table -- the cost of shifting a cosine into a sine wasn't really discernible.

I used floats, assuming that's what you'll likely use in your calculations, given your preference for performance over precision. It doesn't make much difference here, since the bottleneck is really just the array lookup.

Here are my benchmarks:

public static void main(String[] args) {
    int reps = 1<<23;
    int sets = 4;

    Q.pl("  Trial  sinTab  cosTab  sinLib");
    for(int i = 0; i<sets; i++) {
        Q.pf("%7d\t%7.2f\t%7.2f\t%7.2f\n", i, testSinTab(reps), testCosTab(reps), testSinLib(reps));
    }
}

private static float[] sample(int n) {
    Random rand = new Random();
    float[] values = new float[n];
    for (int i=0; i<n; i++) {
        values[i] = 400*(rand.nextFloat()*2-1);
    }
    return values;
}
private static float testSinTab(int n) {
    float[] sample = sample(n);
    long time = -System.nanoTime();
    for (int i=0; i<n; i++) {
        sample[i] = sin(sample[i]);
    }
    time += System.nanoTime();
    return (time/1e6f);
}
private static float testCosTab(int n) {
    float[] sample = sample(n);
    long time = -System.nanoTime();
    for (int i=0; i<n; i++) {
        sample[i] = cos(sample[i]);
    }
    time += System.nanoTime();
    return time/1e6f;
}
private static float testSinLib(int n) {
    float[] sample = sample(n);
    long time = -System.nanoTime();
    for (int i=0; i<n; i++) {
        sample[i] = (float) Math.sin(sample[i]);
    }
    time += System.nanoTime();
    return time/1e6f;
}

output:

  Trial  sinTab  cosTab  sinLib
      0  102.51  111.19  596.57
      1   93.72   92.20  578.22
      2  100.06  107.20  600.68
      3  103.65  102.67  629.86



回答3:


You can try http://sourceforge.net/projects/jafama/

It uses look-up tables, so it might actually be slower than Math, especially if the tables are often evicted from CPU cache, but for thousands of successive calls it can be quite faster.

It also seems slower during class load (maybe the JIT doesn't kicks in then yet), so you might want to avoid it in that particular use-case.




回答4:


I know this question is old, but I think it's the fastest java implementation sintable with precision to 65536 elements.

public class MathHelper {

    private static double[] a = new double[65536];

    public static final double sin(float f) {
        return a[(int) (f * 10430.378F) & '\uffff'];
    }

    public static final double cos(float f) {
        return a[(int) (f * 10430.378F + 16384.0F) & '\uffff'];
    }

    static {
        for (int i = 0; i < 65536; ++i) {
            a[i] = Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D);
        }
    }
}

Source: https://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/MathHelper.java



来源:https://stackoverflow.com/questions/16930581/fast-sine-and-cosine-function-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!