当前位置:  开发笔记 > 编程语言 > 正文

Java的快速超越/三角函数

如何解决《Java的快速超越/三角函数》经验,为你挑选了3个好方法。

由于java.lang.Math中的三角函数非常慢:是否有一个快速和良好近似的库?似乎可以在不损失太多精度的情况下快速进行几次计算.(在我的机器上,乘法需要1.5ns,而java.lang.Math.sin需要46ns到116ns).遗憾的是,还没有办法使用硬件功能.

更新:功能应该足够准确,比如GPS计算.这意味着您需要至少7个十进制数字的精度,这排除了简单的查找表.它应该比基本x86系统上的java.lang.Math.sin快得多.否则就没有意义了.

对于pi/4以上的值,除硬件功能外,Java 还会进行一些昂贵的计算.这样做是有充分理由的,但有时你更关心速度而不是最后一位精度.



1> Darius Bacon..:

哈特的计算机近似.将Chebyshev经济的近似公式列表为不同精度的一系列函数.

编辑:将我的副本从架子上拿下来,结果证明这是一本听起来非常相似的书.这是使用其表格的sin函数.(在C中测试,因为这对我来说更方便.)我不知道这是否会比Java内置更快,但至少可以保证它不那么准确.:)您可能需要先缩小参数范围; 看约翰库克的建议.这本书还有arcsin和arctan.

#include 
#include 

// Return an approx to sin(pi/2 * x) where -1 <= x <= 1.
// In that range it has a max absolute error of 5e-9
// according to Hastings, Approximations For Digital Computers.
static double xsin (double x) {
  double x2 = x * x;
  return ((((.00015148419 * x2
             - .00467376557) * x2
            + .07968967928) * x2
           - .64596371106) * x2
          + 1.57079631847) * x;
}

int main () {
  double pi = 4 * atan (1);
  printf ("%.10f\n", xsin (0.77));
  printf ("%.10f\n", sin (0.77 * (pi/2)));
  return 0;
}



2> finnw..:

这是一组用于快速逼近trig函数的低级技巧.在C中有一些示例代码,我觉得很难遵循,但这些技术在Java中很容易实现.

这是我在Java中的invsqrt和atan2的等效实现.

我可以为其他trig函数做类似的事情,但我没有发现它是必要的,因为分析显示只有sqrt和atan/atan2是主要的瓶颈.

public class FastTrig
{
  /** Fast approximation of 1.0 / sqrt(x).
   * See http://www.beyond3d.com/content/articles/8/
   * @param x Positive value to estimate inverse of square root of
   * @return Approximately 1.0 / sqrt(x)
   **/
  public static double
  invSqrt(double x)
  {
    double xhalf = 0.5 * x; 
    long i = Double.doubleToRawLongBits(x);
    i = 0x5FE6EB50C7B537AAL - (i>>1); 
    x = Double.longBitsToDouble(i);
    x = x * (1.5 - xhalf*x*x); 
    return x; 
  }

  /** Approximation of arctangent.
   *  Slightly faster and substantially less accurate than
   *  {@link Math#atan2(double, double)}.
   **/
  public static double fast_atan2(double y, double x)
  {
    double d2 = x*x + y*y;

    // Bail out if d2 is NaN, zero or subnormal
    if (Double.isNaN(d2) ||
        (Double.doubleToRawLongBits(d2) < 0x10000000000000L))
    {
      return Double.NaN;
    }

    // Normalise such that 0.0 <= y <= x
    boolean negY = y < 0.0;
    if (negY) {y = -y;}
    boolean negX = x < 0.0;
    if (negX) {x = -x;}
    boolean steep = y > x;
    if (steep)
    {
      double t = x;
      x = y;
      y = t;
    }

    // Scale to unit circle (0.0 <= y <= x <= 1.0)
    double rinv = invSqrt(d2); // rinv ? 1.0 / hypot(x, y)
    x *= rinv; // x ? cos ?
    y *= rinv; // y ? sin ?, hence ? ? asin y

    // Hack: we want: ind = floor(y * 256)
    // We deliberately force truncation by adding floating-point numbers whose
    // exponents differ greatly.  The FPU will right-shift y to match exponents,
    // dropping all but the first 9 significant bits, which become the 9 LSBs
    // of the resulting mantissa.
    // Inspired by a similar piece of C code at
    // http://www.shellandslate.com/computermath101.html
    double yp = FRAC_BIAS + y;
    int ind = (int) Double.doubleToRawLongBits(yp);

    // Find ? (a first approximation of ?) from the LUT
    double ? = ASIN_TAB[ind];
    double c? = COS_TAB[ind]; // cos(?)

    // sin(?) == ind / 256.0
    // Note that s? is truncated, hence not identical to y.
    double s? = yp - FRAC_BIAS;
    double sd = y * c? - x * s?; // sin(?-?) ? sin? cos? - cos? sin?

    // asin(sd) ? sd + ?sd³ (from first 2 terms of Maclaurin series)
    double d = (6.0 + sd * sd) * sd * ONE_SIXTH;
    double ? = ? + d;

    // Translate back to correct octant
    if (steep) { ? = Math.PI * 0.5 - ?; }
    if (negX) { ? = Math.PI - ?; }
    if (negY) { ? = -?; }

    return ?;
  }

  private static final double ONE_SIXTH = 1.0 / 6.0;
  private static final int FRAC_EXP = 8; // LUT precision == 2 ** -8 == 1/256
  private static final int LUT_SIZE = (1 << FRAC_EXP) + 1;
  private static final double FRAC_BIAS =
    Double.longBitsToDouble((0x433L - FRAC_EXP) << 52);
  private static final double[] ASIN_TAB = new double[LUT_SIZE];
  private static final double[] COS_TAB = new double[LUT_SIZE];

  static
  {
    /* Populate trig tables */
    for (int ind = 0; ind < LUT_SIZE; ++ ind)
    {
      double v = ind / (double) (1 << FRAC_EXP);
      double asinv = Math.asin(v);
      COS_TAB[ind] = Math.cos(asinv);
      ASIN_TAB[ind] = asinv;
    }
  }
}


@dotancohen,因为我使用这些希腊字母只是为了建立良好的意义(即大多数英语使用者都知道有一些数学知识.)
喜欢unicode变量名!实际上,它们使数学代码更加清晰!

3> 小智..:

这可能会成为:http://sourceforge.net/projects/jafama/

推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有