X86程序集中的浮点除法给出了奇怪的结果

时间:2013-01-08 14:46:03

标签: assembly floating-point x86 fasm

我正在学习Simply FPU tutorial。因此,对于我自己的锻炼,我想学习如何在装配中划分浮点数。假设我打算将48.6除以17.1。这是代码。

format PE console 4.0
entry main
include 'win32a.inc'

section '.data' data readable writeable
num1 dq 48.6
num2 dq 17.1
result dq ?
fmt db "%g", 10
szBuff db 32 dup (0)

section '.code' code readable executable
main:
fld qword [num1]
fld qword [num2]
fdivp 
fstp qword [result]
invoke printf, fmt, result 
invoke ExitProcess, 0


section '.idata' import data readable
library kernel32,'kernel32.dll', msvcrt,'msvcrt.dll'
import kernel32, ExitProcess,'ExitProcess'
import msvcrt, printf, 'printf'

代码的输出是

  

7.62883e + 265

这里出了什么问题?

根据Jester的建议,我使用OllyDbg检查了代码

ollydbg screenshot

我猜结果是正确的,但不知怎的,它被printf搞砸了?

1 个答案:

答案 0 :(得分:4)

Upvote使用该教程,非常好:)

那里有几个问题:

  • 您的值不会是st(0),而st(7)将是st(1)st(0)。寄存器编号是固定的,它始终位于顶部st(0),但是桶旋转。加载某些内容时,它将为st(0)。如果您之后加载其他内容,则桶会旋转,您之前拥有的内容将移至st(1),当前值将放入st(0)
  • 确保您的汇编程序生成适当大小的指令, 例如fldfst
  • 确保您的invoke宏知道如何传递浮点数 printf
  • 的参数
  • 你没有清理FPU堆栈(这里不影响操作, 这只是一个普遍的问题)

我建议您使用调试器来单步执行代码,这样您就可以看到正在发生的事情,甚至不需要尝试使用printf


更新:在linux上使用带有工作代码的gdb的示例会话(为清晰起见而编辑):

$ cat > div.s
.intel_syntax noprefix
.globl main

.data
num1: .double 48.6
num2: .double 17.1
fmt: .string "%g\n"

.text
main:
    sub esp, 16
    fld qword ptr [num1]    # st(0)=48.6
    fld qword ptr [num2]    # st(0)=17.1, st(1)=48.6
    fdivp                   # st(0)=st(1)/st(0), one item popped
    fstp qword ptr [esp+4]  # store as argument and pop
    mov dword ptr [esp], offset fmt
    call printf
    add esp, 16
    ret
$ gcc -masm=intel -m32 -g div.s -o div
$ ./div
2.84211
$ gdb ./div
GNU gdb (GDB) 7.3.50.20111117-cvs-debian
(gdb) br main
Breakpoint 1 at 0x80483c4: file div.s, line 11.
(gdb) r
Starting program: div 
Breakpoint 1, main () at div.s:11
11          sub esp, 16
(gdb) n
main () at div.s:12
12          fld qword ptr [num1]    # st(0)=48.6
(gdb) 
13          fld qword ptr [num2]    # st(0)=17.1, st(1)=48.6
(gdb) info float
=>R7: Valid   0x4004c266666666666800 +48.60000000000000142      
  R6: Empty   0x00000000000000000000
  R5: Empty   0x00000000000000000000
  R4: Empty   0x00000000000000000000
  R3: Empty   0x00000000000000000000
  R2: Empty   0x00000000000000000000
  R1: Empty   0x00000000000000000000
  R0: Empty   0x00000000000000000000
(gdb) n
14          fdivp                   # st(0)=st(1)/st(0), one item popped
(gdb) info float
  R7: Valid   0x4004c266666666666800 +48.60000000000000142      
=>R6: Valid   0x400388ccccccccccd000 +17.10000000000000142      
  R5: Empty   0x00000000000000000000
  R4: Empty   0x00000000000000000000
  R3: Empty   0x00000000000000000000
  R2: Empty   0x00000000000000000000
  R1: Empty   0x00000000000000000000
  R0: Empty   0x00000000000000000000
(gdb) n
15          fstp qword ptr [esp+4]  # store as argument and pop
(gdb) info float
=>R7: Valid   0x4000b5e50d79435e4e16 +2.842105263157894584      
  R6: Empty   0x400388ccccccccccd000
  R5: Empty   0x00000000000000000000
  R4: Empty   0x00000000000000000000
  R3: Empty   0x00000000000000000000
  R2: Empty   0x00000000000000000000
  R1: Empty   0x00000000000000000000
  R0: Empty   0x00000000000000000000
(gdb) n
16          mov dword ptr [esp], offset fmt
(gdb) info float
  R7: Empty   0x4000b5e50d79435e4e16
  R6: Empty   0x400388ccccccccccd000
  R5: Empty   0x00000000000000000000
  R4: Empty   0x00000000000000000000
  R3: Empty   0x00000000000000000000
  R2: Empty   0x00000000000000000000
  R1: Empty   0x00000000000000000000
=>R0: Empty   0x00000000000000000000

请注意,gdb会打印下一个要执行的指令。 FPU堆栈顶部用箭头标记,根据定义,它总是st(0)。如果有必要,其他人会按顺序递增并循环。第一个转储显示48.6被加载到st(0),因为它标有箭头,其他位置为空。然后,17.1再次加载到st(0),因为箭头已移动(镜筒旋转)。 48.6现在是st(1)FDIVP执行除法并从堆栈中删除一个项目,因此最终结果为st(0),其余为空。 FSTP然后将st(0)作为参数存储printf并将其从堆栈中删除,因此所有寄存器现在都是空的。