2011-09-11 10 views
9

devo implementare asin, acos e atan nell'ambiente dove ho solo seguenti strumenti matematici:Approximating funzioni trigonometriche inverse

  • sinusoidali
  • coseno
  • numeri in virgola
  • elementare aritmetica a virgola fissa (galleggianti non sono disponibile)

Ho anche già una funzione radice quadrata ragionevolmente buona.

Posso usare quelli per implementare le funzioni trigonometriche inverse ragionevolmente efficiente?

Non ho bisogno di precisione troppo grande (i numeri in virgola mobile hanno comunque una precisione molto limitata), l'approssimazione di base andrà bene.

Ho già deciso a metà di andare con la ricerca di tabelle, ma vorrei sapere se esiste un'opzione più ordinata (che non richiede diverse centinaia di righe di codice solo per implementare la matematica di base).

EDIT:

per chiarire le cose: Ho bisogno di eseguire le centinaia di funzioni di volte al telaio a 35 fotogrammi al secondo.

+0

possibile duplicato di [Come funzionano le funzioni trigonometriche?] (Http://stackoverflow.com/questions/345085/how-do-trigonometric-functions-work) –

+0

Il duplicato proposto è più relativo al funzionamento delle funzioni trigonometriche (proprio come Si tratta delle funzioni trigonometriche inverse – Teepeemm

risposta

2

Avete bisogno di una grande precisione per arcsin(x) funzione? In caso contrario, è possibile calcolare arcsin in N nodi e conservare i valori in memoria. Suggerisco di usare la linea di approssimazione. se x = A*x_(N) + (1-A)*x_(N+1) quindi x = A*arcsin(x_(N)) + (1-A)*arcsin(x_(N+1)) dove è noto lo arcsin(x_(N)).

+0

Sì, questa è la ricerca da tavolo di cui stavo parlando in OP. Non vedo una ragione per cui avrei calcolato che durante il runtime, simulerei i valori nel programma, quindi il calcolo reale sarebbe solo un'interpolazione tra due valori. –

2

si potrebbe desiderare di utilizzare approssimazione: utilizzare un infinite series fino a quando la soluzione è abbastanza vicino per voi.

ad esempio:
arcsin(z) = Sigma((2n!)/((2^2n)*(n!)^2)*((z^(2n+1))/(2n+1))) dove n in [0, infinito)

1

Forse una specie di forza bruta intelligente come Rapson newton.

Così, per risolvere asin() si va con più ripida discesa sul peccato()

+0

e si può scegliere il punto di partenza da una piccola tabella di ricerca per accelerare il calcolo –

8

In un punto fisso e nvironment (S15.16) ho usato con successo l'algoritmo CORDIC (vedi Wikipedia per una descrizione generale) per calcolare atan2 (y, x), quindi derivato asin() e acos() da che utilizzano noti identità funzionali che coinvolgono il quadrato radice:

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

risulta che trovare un'utile descrizione dell'iterazione CORDIC per atan2() sul doppio è più difficile che ho pensato. Il seguente sito sembra contenere una descrizione sufficientemente dettagliata, e discute anche due approcci alternativi, tabelle di approssimazione e di ricerca polinomiali:

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

+0

Da wikip edia, CORDIC non usa nemmeno le funzioni trig (neat!); Immagino che quello che hai fatto fosse una ricerca; dato sin(), cos() sembra Newton-Raphson o qualcosa del genere sarebbe meglio? (Richiede un numero minore di iterazioni, sebbene il costo delle iterazioni sia diverso.) – petrelharp

+0

Il motivo per cui ho suggerito di guardare in CORDIC è perché richiede solo aritmetica in virgola fissa. L'uso più comune di CORDIC è probabilmente per implementare sin/cos, è così che ho appreso per la prima volta (nel 1987).Ma anche alcune altre funzioni possono essere calcolate con CORDIC, come atan2. Dal momento che non ho alcun codice in giro per il calcolo di atan2 con CORDIC, ho cercato di trovare un sito Web con abbastanza dettagli che qualcuno potesse basare un'implementazione su di esso. Il link che ho postato sopra era la migliore pagina che potessi trovare tramite un motore di ricerca nell'arco di pochi minuti. – njuffa

0

Usa un'approssimazione polinomiale. Minimi quadrati è più semplice (Microsoft Excel ha) e Chebyshev approssimazione è più preciso.

Questa domanda è stato coperto prima: How do Trigonometric functions work?

1

Dovrebbe essere facile aggiungere il seguente codice al punto fisso. Impiega un rational approximation per calcolare l'arcotangente normalizzato all'intervallo [0 1) (è possibile moltiplicarlo per Pi/2 per ottenere l'arcotangente reale). Quindi, è possibile utilizzare well known identities per ottenere arcsin/arccos dall'arctangent.

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

where b = 0.596227 

L'errore massimo è 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; 
} 

Nel caso in cui avete bisogno di più precisione, c'è un 3 ° ordine funzione razionale:

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 

che ha un errore massimo ravvicinamento delle 0,00,811 mila º

1

Inserendo qui la mia risposta da questo other similar question.

nVidia ha alcuni grandi risorse che ho usato per i miei usi, alcuni esempi: acosasinatan2 etc etc ...

Questi algoritmi producono risultati abbastanza precisi. Ecco un verso l'alto esempio di Python con la loro copia codice incollato in:

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 

Ed ecco i risultati per il confronto:

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

Questo è abbastanza vicino! Moltiplicare per 57.29577951 per ottenere risultati in gradi, anche in base alla formula dei "gradi".

-1

Solo le funzioni continue sono approssimabili dai polinomi. E arcsin (x) è discontinuo nel punto x = 1.same arccos (x). Ma una riduzione dell'intervallo all'intervallo 1, sqrt (1/2) in tal caso evita questa situazione. Abbiamo arcsin (x) = pi/2- arccos (x), arccos (x) = pi/2-arcsin (x). Puoi usare matlab per l'approssimazione minimax.Aproximate solo nell'intervallo [0, sqrt (1/2)] (se l'angolo per quell'arcoseno è una richiesta è maggiore che sqrt (1/2) trova la funzione cos (x) .arctangent solo per x < 1.arctan (x) = pi/2-arctan (1/x).

Problemi correlati