Аппроксимация обратных тригонометрических функций

Я должен реализовать asin, acos и atan в среде, где у меня есть только следующие математические инструменты:

  • синус
  • Косинус
  • элементарная арифметика с фиксированной точкой (числа с плавающей запятой недоступны)

У меня также уже есть достаточно хорошая функция квадратного корня.

могу ли я использовать их для реализации разумно эффективных обратных тригонометрических функций?

Мне не нужен слишком большой точность (числа с плавающей запятой имеют очень ограниченную точность в любом случае), основное приближение будет делать.

Я уже наполовину решил пойти с поиском таблицы, но я хотел бы знать, есть ли какой-то более аккуратный вариант (для реализации базовой математики не требуется несколько сотен строк кода).

EDIT:

на: мне нужно запустить функцию сотни раз на кадр со скоростью 35 кадров в секунду.

9 ответов


вам нужна большая точность для ? Если нет, вы можете рассчитать arcsin в N узлах, и сохранить значения в памяти. Я предлагаю использовать линейную апроксимацию. если x = A*x_(N) + (1-A)*x_(N+1) затем x = A*arcsin(x_(N)) + (1-A)*arcsin(x_(N+1)) здесь arcsin(x_(N)) известно.


в среде с фиксированной точкой (S15.16) я успешно использовал КОРДИЧЕСКИЙ алгоритм (см. Википедию для общего описания) для вычисления atan2(y,x), а затем получил asin () и acos () из этого, используя известные функциональные тождества, которые включают квадратный корень:

asin(x) = atan2 (x, sqrt ((1.0 + x) * (1.0 - x)))
acos(x) = atan2 (sqrt ((1.0 + x) * (1.0 - x)), x)

оказывается, найти полезное описание КОРДИЧЕСКОЙ итерации для atan2 () на двойнике сложнее, чем я думал. Следующий сайт содержит достаточно подробное описание, и также обсуждаются два альтернативных подхода, полиномиальная аппроксимация и таблицы поиска:

http://ch.mathworks.com/examples/matlab-fixed-point-designer/615-calculate-fixed-point-arctangent


вы можете использовать приближение: используйте бесконечные сериалы пока решение не будет достаточно близко для вас.

например:
arcsin(z) = Sigma((2n!)/((2^2n)*(n!)^2)*((z^(2n+1))/(2n+1))) где n в [0, бесконечность)


может быть, какая-то разумная грубая сила, как Ньютон рэпсон.

Итак, для решения asin () вы идете с крутым спуском на sin ()


Expression as definite integrals

http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Expression_as_definite_integrals

вы можете сделать это интегрирование численно с помощью функции квадратного корня, аппроксимируя бесконечным рядом:

Infinite series


должно быть легко добавить следующий код в фиксированную точку. Он использует рациональное приближение для вычисления арктангенса, нормированного к интервалу [0 1) (Вы можете умножить его на Pi/2, чтобы получить реальный арктангенса). Затем, вы можете использовать известные личности чтобы получить arcsin / arccos из арктангенса.

normalized_atan(x) ~ (b x + x^2) / (1 + 2 b x + x^2)

where b = 0.596227

максимальная ошибка 0.1620 º

#include <stdint.h>
#include <math.h>

// Approximates atan(x) normalized to the [-1,1] range
// with a maximum error of 0.1620 degrees.

float norm_atan( float x )
{
    static const uint32_t sign_mask = 0x80000000;
    static const float b = 0.596227f;

    // Extract the sign bit
    uint32_t ux_s  = sign_mask & (uint32_t &)x;

    // Calculate the arctangent in the first quadrant
    float bx_a = ::fabs( b * x );
    float num = bx_a + x * x;
    float atan_1q = num / ( 1.f + bx_a + num );

    // Restore the sign bit
    uint32_t atan_2q = ux_s | (uint32_t &)atan_1q;
    return (float &)atan_2q;
}

// Approximates atan2(y, x) normalized to the [0,4) range
// with a maximum error of 0.1620 degrees

float norm_atan2( float y, float x )
{
    static const uint32_t sign_mask = 0x80000000;
    static const float b = 0.596227f;

    // Extract the sign bits
    uint32_t ux_s  = sign_mask & (uint32_t &)x;
    uint32_t uy_s  = sign_mask & (uint32_t &)y;

    // Determine the quadrant offset
    float q = (float)( ( ~ux_s & uy_s ) >> 29 | ux_s >> 30 ); 

    // Calculate the arctangent in the first quadrant
    float bxy_a = ::fabs( b * x * y );
    float num = bxy_a + y * y;
    float atan_1q =  num / ( x * x + bxy_a + num );

    // Translate it to the proper quadrant
    uint32_t uatan_2q = (ux_s ^ uy_s) | (uint32_t &)atan_1q;
    return q + (float &)uatan_2q;
} 

В случае, если вам нужно больше точности, есть 3-й порядок рационального функция:

normalized_atan(x) ~ ( c x + x^2 + x^3) / ( 1 + (c + 1) x + (c + 1) x^2 + x^3)

where c = (1 + sqrt(17)) / 8

который имеет максимальную ошибку приближения 0.00811 º


отправка здесь моего ответа из этого другой подобный вопрос.

nVidia имеет некоторые большие ресурсы, которые я использовал для собственного использования, несколько примеров:acos asin инструмент atan2 etc etc...

эти алгоритмы дают достаточно точные результаты. Вот прямой пример Python с их копией кода, вставленной в:

import math
def nVidia_acos(x):
    negate = float(x<0)
    x=abs(x)
    ret = -0.0187293
    ret = ret * x
    ret = ret + 0.0742610
    ret = ret * x
    ret = ret - 0.2121144
    ret = ret * x
    ret = ret + 1.5707288
    ret = ret * math.sqrt(1.0-x)
    ret = ret - 2 * negate * ret
    return negate * 3.14159265358979 + ret

и вот результаты для сравнения:

nVidia_acos(0.5)  result: 1.0471513828611643
math.acos(0.5)    result: 1.0471975511965976

Это довольно близко! Умножьте на 57.29577951, чтобы получить результаты в градусах, что также из их формулы "градусов".


используйте полиномиальное приближение. Наименьшие квадраты подходят проще всего (Microsoft Excel), а приближение Чебышева более точно.

этот вопрос был рассмотрен ранее:как работают тригонометрические функции?


только непрерывные функции аппроксимируются многочленами. И вычислить arcsin(X) является discontinous в точке x=1.тот же arccos (x).Но уменьшение диапазона до интервала 1, sqrt (1/2) в этом случае избежать этой ситуации. У нас есть arcsin(x)=pi/2 - arccos(x),arccos(x)=pi/2-arcsin (x).вы можете использовать matlab для минимаксного приближения.Апроксимировать только в диапазоне [0, sqrt(1/2)](если угол для этого arcsin больше, чем sqrt(1/2) найти cos(x).функция арктангенса только для x