快速小角度窦/余弦近似

时间:2013-08-09 12:15:26

标签: performance trigonometry approximation

我正在做一些刚体旋转动力学模拟,这意味着我必须通过小角度计算许多旋转,这在评估三角函数时具有性能瓶颈。现在我通过Taylor(McLaurin)系列来做到这一点:

class double2{
  double x,y;
  // Intristic full sin/cos 
  final void rotate   ( double a){ 
     double x_=x; 
     double ca=Math.cos(a); double sa=Math.sin(a); 
     x=ca*x_-sa*y; y=sa*x_+ca*y; 
  }
  // Taylor 7th-order aproximation
  final void rotate_d7( double a){ 
     double x_=x;
     double a2=a*a;
     double a4=a2*a2;
     double a6=a4*a2;
     double ca= 1.0d - a2  /2.0d + a4  /24.0d  - a6/720.0d;
     double sa=   a  - a2*a/6.0d + a4*a/120.0d - a6*a/5040.0d; 
     x=ca*x_-sa*y; y=sa*x_+ca*y; 
  }
}

但是性能速度的交易并不像我预期的那么大:

                     error(100x dphi=Pi/100 )    time [ns pre rotation]
  v.rotate_d1()   :  -0.010044860504615213    9.314306 ns/op 
  v.rotate_d3()   :   3.2624666136960023E-6  16.268745 ns/op 
  v.rotate_d5()   :  -4.600003294941146E-10  35.433617 ns/op 
  v.rotate_d7()   :   3.416711358283919E-14  49.831547 ns/op 
  v.rotate()      :   3.469446951953614E-16  75.70213  ns/op 

有没有更快的方法如何评估sin()和cos()的近似小角度(如< Pi / 100)

我想的可能是一些理性的系列,或连续的分数近似?你知道任何? (预计算表在这里没有意义)

5 个答案:

答案 0 :(得分:3)

您可能会发现调整计算可以提高性能。 E.g:

const double c7 = -1/5040d;
const double c5 = 1/120d;
const double c3 = -1/6d;

double a2 = a * a;

double sa = (((c7 * a2 + c5) * a2 + c3) * a2 + 1) * a;
// similarly for cos

现在优化者可能正在做一些这样的事情,所以你的里程可能会有所不同。有兴趣了解结果。

答案 1 :(得分:1)

不是优化三角函数,而是看看你是否可以不用它们。刚体仿真往往非常适合矢量数学。

答案 2 :(得分:0)

两种方法:尽可能降低精度(如在视频游戏中一样,如果你的目标是表现,则使用最低的可接受精度)

您应该尝试使用列表值。每次执行一次(当游戏加载?)时,计算一个sin / cosinus /数组,然后在恒定时间内访问。

float cosAlpha = COSINUS[(int)(k*alpha)]; // e.g: k = 1000 

调整k和数组大小以选择角度分辨率与内存占用量。

编辑:不要忘记使用余弦/正弦函数的奇偶校验来避免选项卡中的重复值 edit2:尝试浮动而不是双重。差异对于玩家来说是微不足道的,而性能影响方式也很有趣。测试一下!

答案 3 :(得分:0)

你可以添加一些内联汇编程序吗?针对i386'fsincos'指令可能是最快的方法:

Vector2 unit_vector ( Angle angle ) {
  Vector2 r;

//now the normal processor detection
//and various platform specific vesions

#  if defined (__i386__) && !defined (NO_ASM)
#    if defined __GNUC__
#      define ASM_SINCOS
      asm ("fsincos" : "=t" (r.x), "=u" (r.y) : "0" (angle.radians()));

#    elif defined _MSC_VER
#      define ASM_SINCOS
      double a = angle.radians();
      __asm fld a
      __asm fsincos
      __asm fstp r.x
      __asm fstp r.y
#    endif
#  endif
}
来自here

。 这有一个额外的好处,就是在一次通话中计算sin和cos。

编辑:它是Java。

您的轮换是否适合自我包含,您可以通过JNI一次卸载数千个?否则,这种特定于硬件的方法并不好。

答案 4 :(得分:0)

对于小x(x <0.2弧度),你可以安全地假设sin(x)= x。

最大偏差为0.0013。