Подпись Char ATAN2 и ATAN приближений

По сути, я пытался сделать две функции приближения. В обоих случаях я ввожу компоненты "x" и "y" (чтобы справиться с этими неприятными условиями n/0 и 0/0), и мне нужно получить вывод Signed Char. В случае ATAN2 он должен обеспечивать диапазон +/-PI, а в случае ATAN диапазон должен составлять +/- PI/2.

Я потратил весь вчерашний день, пытаясь обернуть вокруг него голову. После игры в Excel, чтобы найти общий хороший алгоритм, основанный на приближении:

    X * (PI/4 + 0.273 * (1 - |X|)) * 128/PI // Scale factor at end to switch to char format

Я придумал следующий код:

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 из библиотеки после преобразования в формат char со знаком.)

Я получил общую суть этого сайта: http://geekshavefeelings.com/posts/fixed-point-atan2

Кто-нибудь может сказать мне, где я иду не так? И как мне подходить к варианту ATAN (который должен быть более точным, поскольку он рассматривает более половины диапазона) без всего этого безумия.

В настоящее время я использую QT Creator 4.8.1 на Windows. Конечной платформой для этого конкретного фрагмента кода в конечном итоге станет микроконтроллер без FPU, а функции ATAN станут одной из основных используемых функций. Таким образом, эффективность с разумной погрешностью (+/-2 градуса для ATAN2 и +/-1 градус для ATAN. ​​На данный момент это приблизительные оценки, поэтому я могу увеличить диапазон, однако 90 градусов определенно не приемлемо!) игры

Заранее спасибо за любую помощь!

РЕДАКТИРОВАТЬ: просто чтобы уточнить, выходы ATAN2 и ATAN выводят на значение знака со знаком, но диапазоны двух типов - это разные диапазоны.

ATAN2 должен иметь диапазон от -128 (-PI) до 127 (+ PI - PI / 128).

ATAN будет иметь диапазон от -128 (-PI/2) до 127 (+PI/2 - PI/256).

Таким образом, выходные значения из двух могут рассматриваться как два разных типа данных.

Извините за путаницу.

EDIT2: явное преобразование неявных чисел int в константы со знаком.

1 ответ

Решение

Набросок следует. Ниже приведена дополнительная информация.

  1. Полученный угол (Binary Angle Measure) точно математически делит единичную окружность на 8 клиньев. Предполагая от -128 до 127 char, за atan2SC() результат каждого октанта составляет 33 целых числа: от 0 до 32 + смещение. (От 0 до 32, а не от 0 до 31 из-за округления.) Для atan2SC(), результат от 0 до 64. Так что просто сосредоточиться на вычислении результата 1 первичного октанта с x,y входы и от 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) теряет слишком много информации в отделе. Нужно найти приближение atan2 с помощью уравнения, которое использует x,y может быть в форме at2 = (a*y*y + b*y)/(c*x*x + d*x),

  3. Хорошо использовать отрицательное абсолютное значение, как с nabsSC(), Отрицательный диапазон целых чисел соответствует положительному диапазону или превышает его. например, от -128 до -1 против 1 до 127. Используйте отрицательные числа и 0 при вызове at2(),


[Редактировать]

  1. Ниже приведен код с упрощенным алгоритмом выбора октанта. Он тщательно сконструирован, чтобы обеспечить любое отрицание x,y приведет к SCHAR_MIN,SCHAR_MAX Диапазон - при условии 2-х дополнений. Все октанты называют iat2() и здесь можно сделать улучшения для повышения точности. Замечания: iat2() деление на x==0 предотвращается как x не 0 в этот момент. В зависимости от режима округления и если эта вспомогательная функция используется совместно с atanSC() будет диктовать свои детали. Предложите двухкомпонентную линейную таблицу с широким целым числом, математика недоступна, иначе - линейная (ay+b)/(cx+d), Я могу играть с этим больше.

  2. Точность в сравнении с производительностью имеет решающее значение для кода OP, но не достаточно хорошо для меня, чтобы получить оптимальный ответ. Итак, я разместил тестовый драйвер ниже, который оценивает точность деталей iat2() ОП придумывает.

  3. Существует 3 подводных камня. 1) Когда ответ должен быть +180 градусов, OP, кажется, хочет -128 BAM. Но atan2(-1, 0.0) приходит с + пи. Это изменение знака может быть проблемой. Замечания: atan2(-1, -0.0) -> -pi. Ссылка2) Когда ответ чуть меньше +180 градусов, в зависимости от iat2() детали, целочисленный результат BAM равен +128, что имеет тенденцию к переходу в -128. atan2() результат чуть меньше + пи или +128 БАМ. Это граничное условие требует пересмотра окончательного кода inOP. 3) Случай (x=0,y=0) требует специальной обработки. Код выбора октанта находит его.

  4. Код для signed char atanSC(signed char x), если это должно быть быстро, можно использовать несколько if()s и 64-байтовая таблица соответствия. (Предполагая 8-битный подписанный символ). Эта же таблица может быть использована в iat2(),

,

#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;
}

Кстати, забавная проблема.

Другие вопросы по тегам