使用泰勒展开的 sin(x) 汇编代码

标签 assembly x86 trigonometry

在 x86 Linux 中,如何实现 sin(x)在使用泰勒展开的汇编代码中?

最佳答案

您没有说明哪种 CPU 架构,所以我假设为 x86。

最简单(也可能是最低效)的方法是在 RPN 中编写公式,它几乎可以直接映射到 FPU 指令。

例子,

代数公式:x - (x^3/3!) + (x^5/5!)

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

变成:

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

有一些明显的优化策略——
  • 而不是计算 x, xxx,
    每个术语的 xxxxx 等,存储​​一个
    “运行产品”,然后乘以
    每次 x*x
  • 代替
    计算每个的阶乘
    术语,做同样的“运行产品”

  • 这是 x86 FPU 的一些注释代码,每条 FPU 指令之后的注释显示了该指令执行后的堆栈状态,堆栈顶部 (st0) 在左侧,例如:
    fldz ; 0
    fld1 ; 1, 0
    

    --剪断--
    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
    

    运行程序:
    $ ./taylor-sine 0.5 1
    0.4791666667
    $ ./taylor-sine 0.5 5
    0.4794255386
    $ echo "s(0.5)"|bc -l
    .47942553860420300027
    

    关于使用泰勒展开的 sin(x) 汇编代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1252929/

    相关文章:

    assembly - 对 X86 分段感到困惑

    assembly - 实模式程序集 : Print Char to Screen without INT Instruction on Boot

    algorithm - 使用基于主要点的度数获取一些点位置

    gpu - cos-extensions 安装 gpu 无法在 GCP Compute Engine VM 上下载驱动程序签名

    java - 正弦在Java中是如何实现的?

    android - 在汇编中编写 Android 应用程序部件

    DOS 的 GCC 交叉编译器在 C 中产生简单的 "Hello world!"链接器错误

    linux - 设备的内存映射 IO 地址是否映射到进程的内核空间?

    assembly - 如何将二进制整数转换为十六进制字符串?

    linux - 为什么这会产生段错误?它应该只是退出