|
FSIN 和 FCOS 指令
在 FPU 中,基本的三角函数都按照相同的方式实现。这些指令都使用一个隐含的源操作数,它位于 ST0 寄存器中。当函数完成时,结果存放在 ST0 寄存器中。
这些函数唯一难以处理的地方是它们都使用弧度作为源操作数的单位。如果正在处理的应用程序使用角度,那么在使用三角函数指令前,要将其转换为弧度,转换公式为:radians = (degrees * pi) / 180 公式由来:因为 180 个角度对应 pi 弧度。
在 FPU 中,使用下面的代码片段可以完成上面的转换:fsts degree1 # 装角度值到 ST0
fidivs val180 # 除以 180,结果存在 ST0 中
fldpi # 装 pi 到 ST0,上面的除法结果被移到 ST1 中
fmul %st(1), %st(0) #计算弧度值
下面程序演示 FSIN 和 FCOS 指令的应用:
.section .data
degree1:
.float 90.0
val180:
.int 180
.section .bss
.lcomm radian1, 4
.lcomm result1, 4
.lcomm result2, 4
.section .text
.global _start
_start:
nop
finit
flds degree1
fidivs val180
fldpi
fmul %st(1), %st(0)
fsts radian1
fsin
fsts result1
flds radian1
fcos
fsts result2
movl $1, %eax
movl $0, %ebx
int $0x80 在 gdb 里可以看到最终的计算结果:(gdb) x/f &result1
0x80490bc <result1>: 1 # sin(90)
(gdb) x/f &result2
0x80490c0 <result2>: -4.37113883e-08 #cos(90) 注:预先计算 pi/180 的值并且把它存储在 FPU 中要更有效率,因为这样就无需每次都计算这个值。
FSINCOS 指令
如果需要同时获得角的正弦值和余弦值可用FSINCOS 指令。这条指令先把正弦结果放到 ST0 中,然后把余弦结果压入堆栈,这样结果就是正弦值被放到了 ST1 ,而余弦值则存在 ST0。下面程序演示这条指令:
.section .data
degree1:
.float 90.0
val180:
.int 180
.section .bss
.lcomm sinresult, 4
.lcomm cosresult, 4
.section .text
.global _start
_start:
nop
finit
flds degree1
fidivs val180
fldpi
fmul %st(1), %st(0)
fsincos
fstps cosresult #存完后弹出 ST0
fsts sinresult
movl $1, %eax
movl $0, %ebx
int $0x80 在 gdb 中可以看到结果:(gdb) x/f &cosresult
0x80490b0 <cosresult>: -2.71050543e-20
(gdb) x/f &sinresult
0x80490ac <sinresult>: 1 cosresult 的值并不精确的等于 0,但已经非常接近。sinresult 是正确的值 1。
正切和反正切指令 FPTAN 和 FPATAN
FPTAN 使用隐含在 ST0 中的操作数 (同样,角的单位必须是弧度,不能是角度)。正切值计算出来后存在 ST0 中,然后接着压入 1.0 到 FPU 堆栈,结果正切值被下移到 ST1 中,而 ST0 中的值为 1.0 。这样做是因为 Intel 要向下兼容 80287 FPU 协处理器写的应用程序,在那个时候还没有 FSIN 和 FCOS,计算这些值需要正切值的倒数。通过在FPTAN 指令之后使用简单的 FDIVR 指令,就可以计算出余切的值。
FPATAN 使用两个隐含的源操作数。它计算角值ST1/ST0的反正切值,入下图所示:
![]()
然后把结果存放在ST1中,然后弹出FPU堆栈,然后把值移动到ST0。
正切与余切的关系是互为倒数。
tan(x)=y ---- 正切
arctan(y)=x 即 arctan(tan(x))=x ---- 反正切 |
|