java中的快速正弦和余弦函数

时间:2013-06-05 02:24:51

标签: java optimization trigonometry

我知道Math.sin()Math.cos()函数,但我想知道是否有一种方法可以创建(或使用已经存在的)更快的函数,因为我没有关心准确的准确性。我正在寻求执行基本的sin或cos计算,并使其基本上尽可能快地执行。简单地迭代西格玛几次会比Math.sin()快?

4 个答案:

答案 0 :(得分:7)

由于您不太关心准确性将其存储在预先计算或仅计算一次的表中,因此当我想避免调用Math时,这就是我所做的事情。

粗略

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;
}
}

我将循环和方法的优化留给你。

答案 1 :(得分:6)

预先计算的表格是要走的路。这是一个实现:

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));
}

在我的笔记本电脑上,这些速度比Math.sin快约6倍。

我只使用了一张表 - 将余弦变成正弦的成本并不明显。

我使用了浮点数,假设你在计算中可能会使用浮点数,因为你对性能的偏好超过了精度。这里没有太大区别,因为瓶颈实际上只是数组查找。

以下是我的基准:

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;
}

输出:

  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

答案 2 :(得分:3)

你可以试试 http://sourceforge.net/projects/jafama/

它使用查找表,因此它可能实际上更慢 比数学,特别是如果表经常从CPU缓存中逐出, 但对于数以千计的连续呼叫,它可能会更快。

在课堂加载期间它似乎也慢了(也许JIT还没有开始), 所以你可能想在那个特定的用例中避​​免使用它。

答案 3 :(得分:0)

我知道这个问题很老,但我认为这是最快的java实现,可以精确到65536个元素。

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);
        }
    }
}

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