首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带符号Char ATAN2和ATAN逼近

带符号Char ATAN2和ATAN逼近
EN

Stack Overflow用户
提问于 2014-10-05 08:51:43
回答 1查看 1.8K关注 0票数 2

基本上,我一直在尝试做两个近似函数。在这两种情况下,我输入"x“和"y”组件(以处理那些讨厌的n/0和0/0条件),并需要得到一个有符号的Char输出。在ATAN 2的情况下,它应该提供一个+/-PI的范围,而在ATAN的情况下,范围应该是+/- PI/2。

我昨天花了整整一段时间试图把我的头围绕在这上面。在excel中寻找一个基于近似的整体好算法之后:

代码语言:javascript
复制
    X * (PI/4 + 0.273 * (1 - |X|)) * 128/PI // Scale factor at end to switch to char format

我想出了以下代码:

代码语言:javascript
复制
signed char nabsSC(signed char x)
{
    if(x > 0)
        return -x;
    return x;
}

signed char signSC(signed char input, signed char ifZero = 0, signed char scaleFactor = 1)
{
    if(input > 0)
    {return scaleFactor;}

    else if(input < 0)
    {return -scaleFactor;}

    else
    {return ifZero;}
}

signed char divisionSC(signed char numerator, signed char denominator)
{
    if(denominator == 0)                // Error Condition
    {return 0;}
    else
    {return numerator/denominator;}
}

//#######################################################################################

signed char atan2SC(signed char y, signed char x)
{
    // @todo make clearer : the code was deduced through trial and error in excel with brute force... not the best reasoning in the world but hey ho
    if((x == y) && (x == 0))                            // Error Condition
    {return 0;}

                                    // Prepare for algorithm Choice
    const signed char X = abs(x);
    signed char Y = abs(y);
    if(Y > 2)
    {Y = (Y << 1) + 4;}

    const signed char alpha1 = 43;
    const signed char alpha2 = 11;
                                    // Make Choice
    if(X <= Y)                          // x/y Path
    {
        const signed char beta = 64;
        const signed char a = divisionSC(x,y);          // x/y
        const signed char A = nabsSC(a);                // -|x/y|

        const signed char temp = a * (alpha1 + alpha2 * A);     // (x/y) * (32 + ((0.273 * 128) / PI) * (1 - |x/y|)))
                                                        // Small angle approximation of ARCTAN(X)
        if(y < 0)                   // Determine Quadrant
        {return -(temp + beta);}
        else
        {return -(temp - beta);}
    }
    else                                // y/x Path
    {
        const signed char a = divisionSC(y,x);          // y/x
        const signed char A = nabsSC(a);                // -|y/x|

        const signed char temp = a * (alpha1 + alpha2 * A);     // (y/x) * (32 + ((0.273 * 128) / PI) * (1 - |y/x|)))
                                                        // Small angle approximation of ARCTAN(X)

        if(x < 0)                   // Determine Quadrant
        {
            Y = signSC(y, -127, 127);                       // Sign(y)*127, if undefined: use -127
            return temp + Y;
        }
        else
        {return temp;}
    }
}

令我失望的是,实现的错误高达180度,而且几乎在两者之间的任何地方都有错误。(在转换为签名字符格式后,我将它与库中的ATAN2F进行了比较。)

我从这个网站上得到了一个总要旨:http://geekshavefeelings.com/posts/fixed-point-atan2

有人能告诉我哪里出了问题吗?以及我应该如何接近阿坦变体(这应该是更精确的,因为它是看一半的范围),没有这些疯狂。

我目前正在windows上使用currently 4.8.1。这种特定代码的终端平台最终将是一个没有FPU的微控制器,而ATAN函数将是使用的主要功能之一。因此,效率有合理的误差( ATAN2为+/-2度,ATAN为+/-1度)。这些都是猜测,所以我可能会增加范围,然而,90度绝对不能接受!)是游戏的目标。

提前感谢您的帮助!

编辑:为了澄清,ATAN2和ATAN输出的输出到一个有符号的字符值,但是这两种类型的范围是不同的。

ATAN2的范围从-128 (-PI)到127 (+ PI /128)。

ATAN的范围从-128 (-PI/2)到127 (+PI/2-PI/256)。

因此,这两种数据的输出值可以看作是两种不同的数据类型。

抱歉弄乱了。

EDIT2:显式地将隐式int数转换为有符号字符常量。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-10-05 16:11:28

下面是提纲。以下是更多信息。

  1. 结果角(一种二元角度度量)精确地将单位圆划分为8个楔形。假设-128到127 char,对于atan2SC(),每个atan2SC()的结果是33个整数:0到32 +一个偏移量。(0到32,而不是0到31,因为四舍五入。)对于atan2SC(),结果是0到64。因此,只需集中计算x,y输入的1种初级辛烷值和0到64种结果的结果。atan2SC()atan2SC()都可以使用这个辅助函数at2()。对于atan2SC(),要查找中间角a,请使用a = at2(x,y)/2。对于atanSC(),使用a = at2(-128, y)
  2. a = divisionSC(x,y)查找整数商,然后a * (43 + 11 * A)在除法中丢失太多的信息。需要用一个使用x,y的方程找到x,y近似,可能是at2 = (a*y*y + b*y)/(c*x*x + d*x)形式的。
  3. 很好地使用负值绝对值,如nabsSC()。整数的负范围符合或超过正数范围。例如-128到-1比1比127。在调用at2()时使用负数和0。

编辑

  1. 下面是一个简化的八进制选择算法的代码。它是精心构造的,以确保任何否定的x,y将导致SCHAR_MIN,SCHAR_MAX范围-假设2的复杂性。所有的iat2()都叫它,这里是可以改进的地方,以提高精度。注意:iat2()x==0除法是被阻止的,因为此时x不是0。取决于舍入模式,如果此辅助函数与atanSC()共享,则将指定其详细信息。建议一个2片的线性表格是宽整数,数学是不可用的,否则是一个线性(ay+b)/(cx+d)。我可能会玩得更多。
  2. 对于OP的代码来说,精度和性能的重要性是至关重要的,但对我来说,它的传递还不够好,我无法得到一个最佳答案。因此,我在下面发布了一个测试驱动程序,它评估了iat2() OP提供的任何细节的准确性。
  3. 存在3个陷阱。1)当答案是+180度时,OP似乎想要-128 BAM。但是atan2(-1, 0.0)想出了+pi。这一迹象逆转可能是个问题。注:atan2(-1, -0.0) -> -pi。Ref。2)当答案略小于+180度时,取决于iat2()的详细信息,整数BAM的结果是+128,这往往是-128。atan2()结果仅小于+pi或+128个BAM。此边缘条件需要在‘s的最终代码中进行检查。( 3) (x=0,y=0)案件需要特殊处理。重要的选择代码会找到它。
  4. 如果需要快速,signed char atanSC(signed char x)的代码可以使用几个if()和一个64字节的查找表。(假设有8位签名字符)。这个表可以在iat2()中使用。

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

// -x > -y >= 0, so divide by 0 not possible
static signed char iat2(signed char y, signed char x) {
  // printf("x=%4d y=%4d\n", x, y); fflush(stdout);
  return ((y*32+(x/2))/x)*2;  // 3.39 mxdiff
  // return ((y*64+(x/2))/x);    // 3.65 mxdiff
  // return (y*64)/x;            // 3.88 mxdiff
}

signed char iatan2sc(signed char y, signed char x) {
  // determine octant
  if (y >= 0) { // oct 0,1,2,3
    if (x >= 0) { // oct 0,1
      if (x > y) {
        return iat2(-y, -x)/2 + 0*32;
      } else {
        if (y == 0) return 0; // (x=0,y=0)
        return -iat2(-x, -y)/2 + 2*32;
      }
    } else { // oct 2,3
      // if (-x <= y) {
      if (x >= -y) {
        return iat2(x, -y)/2 + 2*32;
      } else {
        return -iat2(-y, x)/2 + 4*32;
      }
    }
  } else { // oct 4,5,6,7
    if (x < 0) { // oct 4,5
      // if (-x > -y) {
      if (x < y) {
        return iat2(y, x)/2 + -4*32;
      } else {
        return -iat2(x, y)/2 + -2*32;
      }
    } else { // oct 6,7
      // if (x <= -y) {
      if (-x >= y) {
        return iat2(-x, y)/2 + -2*32;
      } else {
        return -iat2(y, -x)/2 + -0*32;
      }
    }
  }
}

#include <math.h>

static void test_iatan2sc(signed char y, signed char x) {
  static int mn=INT_MAX;
  static int mx=INT_MIN;
  static double mxdiff = 0;

  signed char i = iatan2sc(y,x);
  static const double Pi = 3.1415926535897932384626433832795;
  double a = atan2(y ? y : -0.0, x) * 256/(2*Pi);

  if (i < mn) {
    mn = i;
    printf ("x=%4d,y=%4d  --> %4d   %f, mn %d mx %d mxdiff %f\n", 
        x,y,i,a,mn,mx,mxdiff);
  }
  if (i > mx) {
    mx = i;
    printf ("x=%4d,y=%4d  --> %4d   %f, mn %d mx %d mxdiff %f\n", 
        x,y,i,a,mn,mx,mxdiff);
  }

  double diff = fabs(i - a);
  if (diff > 128) diff = fabs(diff - 256);

  if (diff > mxdiff) {
    mxdiff = diff;
    printf ("x=%4d,y=%4d  --> %4d   %f, mn %d mx %d mxdiff %f\n", 
        x,y,i,a,mn,mx,mxdiff);
  }
}


int main(void) {
  int x,y;
  int n = 127;
  for (y = -n-1; y <= n; y++) {
    for (x = -n-1; x <= n; x++) {
      test_iatan2sc(y,x);
    }
  }
  puts("Done");
  return 0;
}

顺便说一句:一个有趣的问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26201084

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档