一次计算正弦和余弦

时间:2015-08-04 16:04:20

标签: c++ algorithm trigonometry

我有一个使用相同参数的正弦和余弦的科学代码(我基本上需要该参数的复数指数)。我想知道是否有可能比分别调用正弦和余弦函数更快。

另外我只需要0.1%的精度。那么有什么方法可以找到默认的触发功能并截断功率系列的速度吗?

我想到的另一件事是,有没有办法执行余数运算,结果总是积极的?在我自己的算法中,我使用x=fmod(x,2*pi);,但如果x为负数,我需要加2pi(较小的域意味着我可以使用更短的幂级数)

编辑:LUT被证明是最好的方法,但我很高兴我了解了其他的近似技术。我还建议使用明确的中点近似。这就是我最终做的事情:

const int N = 10000;//about 3e-4 error for 1000//3e-5 for 10 000//3e-6 for 100 000
double *cs = new double[N];
double *sn = new double[N];
for(int i  =0;i<N;i++){
    double A= (i+0.5)*2*pi/N;
    cs[i]=cos(A);
    sn[i]=sin(A);
}

以下部分近似(中点)sincos(2 * pi *(wc2 + t [j] *(cotp * t [j] -wc)))

double A=(wc2+t[j]*(cotp*t[j]-wc));
int B =(int)N*(A-floor(A));
re += cs[B]*f[j];
im += sn[B]*f[j];

另一种方法可能是使用chebyshev分解。您可以使用orthogonality属性来查找系数。针对指数进行了优化,它看起来像这样:

double fastsin(double x){
    x=x-floor(x/2/pi)*2*pi-pi;//this line can be improved, both inside this 
                              //function and before you input it into the function

    double x2 = x*x;
    return (((0.00015025063885163012*x2- 
   0.008034350857376128)*x2+ 0.1659789684145034)*x2-0.9995812174943602)*x;} //7th order chebyshev approx

5 个答案:

答案 0 :(得分:1)

如果你想要使用功率良好(但不是很高)的精确度进行快速评估,你应该使用切比雪夫多项式的扩展:将系数列表(你需要非常少,精度为0.1%)并评估扩展这些多项式的递归关系(它真的很容易)。

参考文献:

  1. 制表系数:http://www.ams.org/mcom/1980-34-149/S0025-5718-1980-0551302-5/S0025-5718-1980-0551302-5.pdf
  2. chebyshev扩展的评估:https://en.wikipedia.org/wiki/Chebyshev_polynomials
  3. 您需要(a)获得&#34;减少&#34;在-pi / 2 .. + pi / 2范围内的参数,然后(b)当参数实际上应该在&#34;其他&#34;中时,处理结果中的符号。完整基本区间的一半-pi .. + pi。这些方面不应成为一个主要问题:

    1. 确定(和#34;记住&#34;作为整数1或-1)原始角度的符号并继续使用绝对值。
    2. 使用模数函数减少到0..2PI
    3. 的区间
    4. 确定(和#34;记住&#34;作为整数1或-1)是否在&#34;秒&#34;一半,如果是,则减去pi * 3/2,否则减去pi / 2。注意:这有效地交换了正弦和余弦(除了符号);在最终评估中考虑到这一点。
    5. 这样就完成了在-pi / 2 .. + pi / 2中获得角度的步骤 在使用Cheb-expansions评估正弦和余弦后,应用&#34;标志&#34;上面的步骤1和3,以获得值的正确标志。

答案 1 :(得分:1)

只需创建一个查找表。以下内容将允许您查找-2PI和2PI之间的任何弧度值的sin和cos。

// LOOK UP TABLE
var LUT_SIN_COS = [];
var N = 14400;
var HALF_N = N >> 1;
var STEP = 4 * Math.PI / N;
var INV_STEP = 1 / STEP;
// BUILD LUT
for(var i=0, r = -2*Math.PI; i < N; i++, r += STEP) {
    LUT_SIN_COS[2*i] = Math.sin(r);
    LUT_SIN_COS[2*i + 1] = Math.cos(r);
}

通过以下方式索引到查找表中:

var index = ((r * INV_STEP) + HALF_N) << 1;
var sin = LUT_SIN_COS[index];
var cos = LUT_SIN_COS[index + 1];

这是一个小提示,显示不同大小LUT http://jsfiddle.net/77h6tvhj/

可以预期的%误差

编辑这是一个ideone(c ++),带有~base~对浮点sin和cos。 http://ideone.com/SGrFVG无论ideone.com的基准值是多少,LUT都要快5倍。

答案 2 :(得分:0)

一种方法是学习如何实现CORDIC算法。这不是困难而且非常有趣。这给你余弦和正弦。维基百科给出了MATLAB example,它应该很容易在C ++中适应。

请注意,只需降低参数n即可增加速度并降低精度。

关于你的第二个问题,它已经被问到here(在C中)。似乎没有简单的方法。

答案 3 :(得分:0)

您还可以使用平方根计算正弦,给定角度和余弦。

以下示例假设角度范围为0到2π:

 double c = cos(angle);
 double s = sqrt(1.0-c*c);
 if(angle>pi)s=-s;

答案 4 :(得分:0)

对于单精度浮点数,Microsoft对正弦使用11度多项式逼近,余弦使用10度:XMScalarSinCos。 它们还有更快的版本,XMScalarSinCosEst,它使用低次多项式。

如果您不在Windows上,则会在Boost许可下找到相同的代码+系数on geometrictools.com