2009-08-10 6 views

Respuesta

7

¿Te puede ayudar este artículo?

http://www.coranac.com/2009/07/sines/

Tiene un par de algoritmos para el cálculo de pecado aproximada (x) los valores, con ambas versiones de montaje C y. De acuerdo, es ensamblado ARM, pero la esencia de esto debería traducirse fácilmente a x86 o similar.

+0

excelente enlace. ¡Gracias! – kenny

+0

Alguien me señala en la dirección de matemáticas para principiantes .. por favor.Ese enlace me está dando pesadillas :) –

+0

@ dark-star1: echa un vistazo a http://khanacademy.org - Hay un conjunto de videos sobre funciones aproximadas a mitad de camino en la lista de Cálculo, y hay videos para cada nivel de matemáticas previos lo. – Cogwheel

9

No indicas qué arquitectura de CPU, por lo tanto, asumo x86.

La forma simplista (y posiblemente la más ineficiente) sería escribir la fórmula en RPN, que se puede asignar casi directamente a las instrucciones FPU.

ejemplo,

fórmula algebraica: x - (! X^3/3) + (! X^5/5)

RPN: xxx * x * 3 2 */- xx * x * x * x * 5 * 4 * 3 * 2/+

que se convierte en:

fld x 
fld x 
fld x 
fmul 
fld x 
fmul 
fild [const_3] 
fild [const_2] 
fmul 
fdiv 
fsub 
fld x 
fld x 
fmul 
fld x 
fmul 
fld x 
fmul 
fld x 
fmul 
fild [const_5] 
fild [const_4] 
fmul 
fild [const_3] 
fmul 
fild [const_2] 
fmul 
fdiv 
fadd 

Hay algunas estrategias de optimización obvias -

  • en lugar de calcular x, x x x, x x x x x etc para cada término, almacenar un 'producto correr' y simplemente se multiplican por x * x cada vez
  • vez de calcular el factorial para cada plazo, hacer lo mismo 'producto de marcha'

Aquí hay algo de código comentado para x86 FPU, los comentarios después de cada instrucción de FPU mostrar el estado de pila después de que la instrucción tiene ex ecuted, con la parte superior de pila (st0) a la izquierda, por ejemplo:

fldz ; 0 
fld1 ; 1, 0 

--snip--

bits 32 

section .text 

extern printf 
extern atof 
extern atoi 
extern puts 
global main 

taylor_sin: 
    push eax 
    push ecx 

    ; input : 
    ; st(0) = x, value to approximate sin(x) of 
    ; [esp+12] = number of taylor series terms 

    ; variables we'll use : 
    ; s = sum of all terms (final result) 
    ; x = value we want to take the sin of 
    ; fi = factorial index (1, 3, 5, 7, ...) 
    ; fc = factorial current (1, 6, 120, 5040, ...) 
    ; n = numerator of term (x, x^3, x^5, x^7, ...) 

    ; setup state for each iteration (term) 
    fldz ; s x 
    fxch st1 ; x s 
    fld1 ; fi x s 
    fld1 ; fc fi x s 
    fld st2 ; n fc fi x s 

    ; first term 
    fld st1 ; fc n fc fi x s 
    fdivr st0,st1 ; r n fc fi x s 
    faddp st5,st0 ; n fc fi x s 

    ; loop through each term 
    mov ecx,[esp+12] ; number of terms 
    xor eax,eax ; zero add/sub counter 

loop_term: 
    ; calculate next odd factorial 
    fld1 ; 1 n fc fi x s 
    faddp st3 ; n fc fi x s 
    fld st2 ; fi n fc fi x s 
    fmulp st2,st0 
    fld1 ; 1 n fc fi x s 
    faddp st3 ; n fc fi x s 
    fld st2 ; fi n fc fi x s 
    fmulp st2,st0 ; n fc fi x s 

    ; calculate next odd power of x 
    fmul st0,st3 ; n*x fc fi x s 
    fmul st0,st3 ; n*x*x fc fi x s 

    ; divide power by factorial 
    fld st1 ; fc n fc fi x s 
    fdivr st0,st1 ; r n fc fi x s 

    ; check if we need to add or subtract this term 
    test eax,1 
    jnz odd_term 
    fsubp st5,st0 ; n fc fi x s 
    jmp skip 
odd_term: 
    ; accumulate result 
    faddp st5,st0 ; n fc fi x s 
skip: 
    inc eax ; increment add/sub counter 
    loop loop_term 

    ; unstack work variables 
    fstp st0 
    fstp st0 
    fstp st0 
    fstp st0 

    ; result is in st(0) 

    pop ecx 
    pop eax 

    ret 

main: 

    ; check if we have 2 command-line args 
    mov eax, [esp+4] 
    cmp eax, 3 
    jnz error 

    ; get arg 1 - value to calc sin of 
    mov ebx, [esp+8] 
    push dword [ebx+4] 
    call atof 
    add esp, 4 

    ; get arg 2 - number of taylor series terms 
    mov ebx, [esp+8] 
    push dword [ebx+8] 
    call atoi 
    add esp, 4 

    ; do the taylor series approximation 
    push eax 
    call taylor_sin 
    add esp, 4 

    ; output result 
    sub esp, 8 
    fstp qword [esp] 
    push format 
    call printf 
    add esp,12 

    ; return to libc 
    xor eax,eax 
    ret 

error: 
    push error_message 
    call puts 
    add esp,4 
    mov eax,1 
    ret 

section .data 

error_message: db "syntax: <x> <terms>",0 
format: db "%0.10f",10,0 

ejecutar el programa:

$ ./taylor-sine 0.5 1 
0.4791666667 
$ ./taylor-sine 0.5 5 
0.4794255386 
$ echo "s(0.5)"|bc -l 
.47942553860420300027 
6

¿Por qué? Ya existe un código de operación FCOS y FSIN ya que el (alrededor de 1987) procesador fuente 80387

:

http://ref.x86asm.net/coder32.html

Wikipedia

Amigo personal de demoscene

+0

Probablemente sea un ejercicio de tarea. –

Cuestiones relacionadas