2011-09-11 8 views
9

tengo que poner en práctica asin, acos y atan en el entorno donde sólo he siguientes herramientas matemáticas:que se aproximan a las funciones trigonométricas inversas

  • seno
  • coseno
  • números de punto
  • elemental aritmética de punto fijo (flotantes no son disponible)

También tengo una función de raíz cuadrada razonablemente buena.

¿Se pueden usar para implementar funciones trigonométricas inversas razonablemente eficientes?

No necesito una precisión demasiado grande (los números de coma flotante tienen una precisión muy limitada), la aproximación básica servirá.

Ya estoy medio decidido a buscar tablas, pero me gustaría saber si hay alguna opción más ordenada (que no necesita varios cientos de líneas de código solo para implementar las matemáticas básicas).

EDIT:

para aclarar las cosas: necesito para ejecutar la función cientos de veces por trama a 35 fotogramas por segundo.

+0

posible duplicado de [¿Cómo funcionan las funciones trigonométricas?] (Http://stackoverflow.com/questions/345085/how-do-trigonometric-functions-work) –

+0

El duplicado propuesto es más acerca de cómo funcionan las funciones trigonométricas (al igual que es título). Se trata de las funciones trigonométricas inversas – Teepeemm

Respuesta

2

¿Necesita una gran precisión para arcsin(x) función? Si no, puede calcular arcsin en N nodos, y mantener los valores en la memoria. Sugiero usar la aproximación de línea. si es x = A*x_(N) + (1-A)*x_(N+1) entonces x = A*arcsin(x_(N)) + (1-A)*arcsin(x_(N+1)) donde se conoce arcsin(x_(N)).

+0

Sí, esa es la búsqueda de tabla de la que estaba hablando en OP. no veo una razón por la que calculé que en el tiempo de ejecución, simoly hornearía los valores en el programa, por lo que el cálculo de asin real sería simplemente una interpolación entre dos valores. –

2

es posible que desee utilizar la aproximación: utilice un infinite series hasta que la solución se acerque lo suficiente para usted.

por ejemplo:
arcsin(z) = Sigma((2n!)/((2^2n)*(n!)^2)*((z^(2n+1))/(2n+1))) donde n en [0, infinito)

1

Tal vez algún tipo de fuerza bruta inteligente como Rapson newton.

Así que para resolver asin() vas con el descenso más pronunciado en el pecado()

+0

y puede elegir el punto de partida de una pequeña tabla de búsqueda para acelerar el cálculo –

8

En un punto fijo e nvironment (S15.16) Utilicé con éxito el algoritmo CORDIC (ver Wikipedia para una descripción general) para calcular atan2 (y, x), luego derivar asin() y acos() a partir de eso utilizando identidades funcionales bien conocidas que involucran el cuadrado root:

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

resulta que encontrar una descripción útil de la iteración CORDIC para atan2() en el doble es más difícil de lo que pensaba. El siguiente sitio web parece contener una descripción suficientemente detallada, y también analiza dos enfoques alternativos, mesas de aproximación y de búsqueda de polinomios:

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

+0

desde wikip edia, CORDIC ni siquiera usa las funciones trigonométricas (¡ordenadas!); Imagino que lo que hiciste fue una búsqueda; dado el pecado(), cos() parece que Newton-Raphson o algo así sería mejor? (Se requieren menos iteraciones, aunque el costo de las iteraciones sería diferente). – petrelharp

+0

La razón por la que sugerí consultar CORDIC es porque solo requiere aritmética de punto fijo. El uso más común de CORDIC probablemente sea para implementar sin/cos, así es como lo aprendí por primera vez (en 1987).Pero también se pueden calcular algunas otras funciones con CORDIC, como atan2. Como no tengo ningún código para computar atan2 con CORDIC, traté de encontrar un sitio web con detalles suficientes para que alguien pudiera basar una implementación en él. El enlace que publiqué arriba fue la mejor página que pude encontrar a través de un motor de búsqueda en el espacio de unos minutos. – njuffa

0

utilizar una aproximación polinómica. El ajuste de mínimos cuadrados es más fácil (Microsoft Excel lo tiene) y la aproximación de Chebyshev es más precisa.

Esta pregunta ha sido cubierto antes: How do Trigonometric functions work?

1

Debería ser fácil agregar el siguiente código al punto fijo. Emplea un rational approximation para calcular el arcotangente normalizado al intervalo [0 1) (puede multiplicarlo por Pi/2 para obtener el arcotangente real). Luego, puede usar well known identities para obtener los arcosin/arccos del arcotangente.

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

where b = 0.596227 

El error máximo es 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; 
} 

En caso de necesitar más precisión, hay una función racional de 3er orden:

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 

que tiene un error máximo aproximación de 0,00811 º

1

enviando aquí mi respuesta de este other similar question.

nVidia tiene algunos grandes recursos que he utilizado para mis propios usos, algunos ejemplos: acosasinatan2, etc, etc ...

Estos algoritmos producir suficientes resultados precisos. He aquí un ejemplo de Python hacia arriba con su copia código pegado en:

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 

Y aquí están los resultados para la comparación:

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

que es bastante cerca! Multiplique por 57.29577951 para obtener resultados en grados, que también es de su fórmula de "grados".

-1

Solo las funciones continuas son aproximadas por polinomios. Y arcsin (x) es discontinuo en el punto x = 1.same arccos (x) .Pero una reducción de rango al intervalo 1, sqrt (1/2) en ese caso evita esta situación. Tenemos arcsin (x) = pi/2- arccos (x), arccos (x) = pi/2-arcsin (x). Puedes usar matlab para la aproximación de minimax. Aproximadamente solo en el rango [0, sqrt (1/2)] (si el ángulo para ese arco es solicitud es mayor que sqrt (1/2) encuentre la función cos (x) .arctangent solo para x < 1.arctan (x) = pi/2-arctan (1/x).

Cuestiones relacionadas