FPTAN 示例 x86

FPTAN Example x86

根据 Intel 文档,这就是 FPTAN 的作用:

Replace ST(0) with its approximate tangent and push 1 onto the FPU stack.

这是我在 NASM 中编写的代码:

section .data
    fVal: dd 4
    fSt0: dq 0.0
    fSt1: dq 0.0

section .text
    fldpi
    fdiv  dword[fVal]  ; divide pi by 4 and store result in ST(0).
    fptan
    fstp  qword[fSt0]  ; store ST(0)
    fstp  qword[fSt1]  ; store ST(1)

此时我发现fSt0fSt1的值是:

fSt0 = 5.60479e+044
fSt1 = -1.#IND

但是,fSt0fSt1 不应该都是 1 吗?

正如 Michael Petch 已经在评论中指出的那样,您有一个简单的错字。您没有将 fVal 声明为浮点值(如预期的那样),而是将其声明为 32 位整数。变化:

fVal: dd 4

至:

fVal: dd 4.0

那么您的代码将按预期工作。写对了。

如果您想要 接受整数输入,您可以通过更改代码以使用 FIDIV 指令来实现。该指令首先将整数转换为双精度浮点值,然后进行除法:

fldpi
fidiv  dword [fVal]    ; st(0) = pi / fVal
fptan                  ; st(0) = tan(st(0))
                       ; st(1) = 1.0
fstp   qword [fSt0]
fstp   qword [fSt1]

但是因为需要转换,所以这比将输入作为浮点值的效率稍低。

请注意,如果您打算这样做,在某些较旧的 CPU 上分解负载会更有效,以便它与除法分开完成——例如,

fldpi
fild   dword [fVal]
fdivp  st(1), st(0)    ; st(0) = pi / fVal
fptan                  ; st(0) = tan(st(0))
                       ; st(1) = 1.0
fstp   qword [fSt0]
fstp   qword [fSt1]

换句话说,我们将 FIDIV 指令拆分为单独的 FILD(整数加载)和 FDIVP(分拆和弹出)指令。这改善了重叠,从而从代码的执行速度中削减了几个时钟周期。 (在较新的 CPU 上,从 AMD 系列 15h [Bulldozer] 和 Intel Pentium II 及更高版本 - 将 FIDIV 分解为 FILD+FDIV 并没有真正的优势;无论哪种方式,你都应该性能相同。)

当然,因为这里的所有内容都是 常量,并且 tan(pi/4) == 1,您的代码等同于:

fld1
fld1

…这是优化编译器会生成的。 :-)