0

我正在做一些刚体旋转动力学模拟,这意味着我必须以小角度计算许多旋转,这在三角函数的评估中存在性能瓶颈。现在我按 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 )

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

4

5 回答 5

3

您可能会发现调整计算可以提高性能。例如:

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

现在优化器可能会自己做一些事情,所以你的里程可能会有所不同。有兴趣知道两种方式的结果。

于 2013-08-09T12:56:12.433 回答
1

与其优化三角函数,不如看看你是否可以不用它们。刚体模拟往往非常适合矢量数学。

于 2013-08-09T14:53:31.857 回答
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
}

这里。这具有在一次调用中计算 sin 和 cos 的额外好处。

编辑:它是Java。

您的轮换是否适当地自给自足,可以通过 JNI 一次卸载数千个?否则,这种特定于硬件的方法是不好的。

于 2013-08-09T13:10:13.953 回答
0

两种方法:尽可能降低精度(在视频游戏中经常使用,如果您瞄准性能,则使用可接受的最小精度)

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

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

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

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

于 2013-08-09T12:38:44.807 回答
0

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

最大偏差为 0.0013。

于 2016-09-12T16:57:26.927 回答