【问题标题】:Calculate tan having sin/cos LUT计算具有 sin/cos LUT 的 tan
【发布时间】:2012-12-08 18:47:51
【问题描述】:
#define PARTPERDEGREE 1
double mysinlut[PARTPERDEGREE * 90 + 1];
double mycoslut[PARTPERDEGREE * 90 + 1];
void MySinCosCreate()
{
    int i;
    double angle, angleinc;

    // Each degree also divided into 10 parts
    angleinc = (M_PI / 180) / PARTPERDEGREE;
    for (i = 0, angle = 0.0; i <= (PARTPERDEGREE * 90 + 1); ++i, angle += angleinc)
    {
        mysinlut[i] = sin(angle);
    }

    angleinc = (M_PI / 180) / PARTPERDEGREE;
    for (i = 0, angle = 0.0; i <= (PARTPERDEGREE * 90 + 1); ++i, angle += angleinc)
    {
        mycoslut[i] = cos(angle);
    }
}


double MySin(double rad)
{
    int ix;
    int sign = 1;
    double angleinc = (M_PI / 180) / PARTPERDEGREE;

    if(rad > (M_PI / 2))
        rad = M_PI / 2 - (rad - M_PI / 2);

    if(rad < -(M_PI / 2))
        rad = -M_PI / 2 - (rad + M_PI / 2);

    if(rad < 0)
    {
        sign = -1;
        rad *= -1;
    }

    ix = (rad * 180) / M_PI * PARTPERDEGREE;
    double h = rad - ix*angleinc;
    return sign*(mysinlut[ix] + h*mycoslut[ix]);
}

double MyCos(double rad)
{
    int ix;
    int sign = 1;
    double angleinc = (M_PI / 180) / PARTPERDEGREE;


    if(rad > M_PI / 2)
    {
        rad = M_PI / 2 - (rad - M_PI / 2);
        sign = -1;
    }
    else if(rad < -(M_PI / 2))
    {
        rad = M_PI / 2 + (rad + M_PI / 2);
        sign = -1;
    }
    else if(rad > -M_PI / 2 && rad < M_PI / 2)
    {   
        rad = abs(rad);
        sign = 1;
    }

    ix = (rad * 180) / M_PI * PARTPERDEGREE;

    double h = rad - ix*angleinc;
    return sign*(mycoslut[ix] - h*mysinlut[ix]);
}

double MyTan(double rad)
{
    return MySin(rad) / MyCos(rad);
}

事实证明,使用除法计算 tan 比原来的 tan 函数更昂贵。

有没有什么方法可以在没有除法运算的情况下使用 sin/cos 查找表值计算 tan,因为在我的 MCU 上除法成本很高。

拥有tan LUT 并使用 tan/sin 或 tan/cos 提取结果是否更好,就像现在对 sin/cos 所做的那样?

【问题讨论】:

  • 除法很昂贵 - 为什么不使用另一个 LUT 进行 tan 呢?您也可以只对 sin/cos 使用一个 LUT 而不是两个。对于这些 LUT 使用双精度可能没有意义 - 如果您对性能比对准确性更感兴趣,浮点数应该足够好。
  • @PaulR:首先没有逼近sin (x+h) ≈ sin x + h*cos xcos (x+h) ≈ cos x - h*sin x 精度是不够的。其次,我不确定如何使用tan 用 sin 或 cos LUT 来近似它。需要一些实际的帮助。
  • 不过,如果除法太慢,为tan 建立一个查找表(那么你还需要sincos 表吗?)。然后,您可以使用导数tan (x+h) ≈ tan x + h*(1 + tan² x) 或线性插值double t = (rad * 180) / M_PI * PARTPERDEGREE; ix = (int)t; t -= ix; return t*mytanlut[ix+1] + (1-t)*mytanlut[ix]; 进行近似。线性插值通常会提供更好的近似值,但如果两个角度跨越π/2,则会产生垃圾,因为一个值将非常大且为正,另一个值非常大且为负[可能为±∞]。跨度>
  • @DanielFischer:让我尝试使用这两种方法。实际上我还是需要 sin/cos 表,因为我在其他地方使用 sin/cos。一如既往,谢谢!

标签: c optimization


【解决方案1】:

尤其是在微控制器中,除法 y/x 通常可以通过 log & exp 表或迭代乘法进行优化,直到分母 1 +- eps = 1 +- x^(2^n) 为零。

y/X = y / (1-x) 
    = (1+x)y / (1+x)(1-x)
    = (1+x)(1+x^2)y / (1-x^2)(1+x^2)
    = (1+x)(1+x^2)(1+x^4)y / (1-x^4)(1+x^4)

【讨论】: