x86汇编程序:浮点比较

时间:2011-08-14 14:53:40

标签: x86 floating-point compare assembly gas

作为编译器项目的一部分,我必须为x86编写GNU汇编代码来比较浮点值。我试图找到有关如何在线进行此操作的资源,据我所知,它的工作原理如下:

假设我要比较的to值是浮点堆栈上的唯一值,那么fcomi指令将比较这些值并设置CPU标志,以便je, jne, jl, ...可以使用说明。

我问,因为这只会有效。例如:

.section    .data
msg:    .ascii "Hallo\n\0"
f1:     .float 10.0
f2:     .float 9.0

.globl main
    .type   main, @function
main:
    flds f1
    flds f2
    fcomi
    jg leb
    pushl $msg
    call printf
    addl $4, %esp
leb:
    pushl $0
    call exit

即使我认为应该打印“Hallo”,如果你切换f1和f2,它仍然不会是一个逻辑矛盾。然而,jejne似乎工作得很好。

我做错了什么?

PS:fcomip只弹出一个值还是弹出两个?

1 个答案:

答案 0 :(得分:38)

这一切都来自 Intel 64 and IA-32 Architectures Software Developer's Manuals第2卷。

FCOMI仅设置CMP执行的部分标记。您的代码有%st(0) == 9%st(1) == 10。 (因为它是一个堆栈,它们被加载到),参考第2A卷第3-348页的表格,您可以看到这是“ST0< ST(i)”的情况,因此它将清除ZF和PF以及设置CF.同时在pg。 3-544卷在图2A中,您可以读到JG表示“如果更大则跳跃(ZF = 0且SF = OF)”。换句话说,它正在测试符号,溢出和零标志,但FCOMI没有设置符号或溢出!

根据您希望跳跃的条件,您应该查看可能的比较结果,并决定何时跳跃。

+--------------------+---+---+---+
| Comparison results | Z | P | C |
+--------------------+---+---+---+
| ST0 > ST(i)        | 0 | 0 | 0 |
| ST0 < ST(i)        | 0 | 0 | 1 |
| ST0 = ST(i)        | 1 | 0 | 0 |
+--------------------+---+---+---+

我已经制作了这张小桌子,以便更容易理解:

+--------------+---+---+-----+------------------------------------+
| Test         | Z | C | Jcc | Notes                              |
+--------------+---+---+-----+------------------------------------+
| ST0 < ST(i)  | X | 1 | JB  | ZF will never be set when CF = 1   |
| ST0 <= ST(i) | 1 | 1 | JBE | Either ZF or CF is ok              |
| ST0 == ST(i) | 1 | X | JE  | CF will never be set in this case  |
| ST0 != ST(i) | 0 | X | JNE |                                    |
| ST0 >= ST(i) | X | 0 | JAE | As long as CF is clear we are good |
| ST0 > ST(i)  | 0 | 0 | JA  | Both CF and ZF must be clear       |
+--------------+---+---+-----+------------------------------------+
Legend: X: don't care, 0: clear, 1: set

换句话说,条件代码与使用无符号比较的条件代码相匹配。如果您使用的是FMOVcc,也会如此。

如果fcomi的任一(或两者)操作数是NaN,则设置ZF=1 PF=1 CF=1。 (FP比较有4种可能的结果:><==或无序结果。如果您关心代码对NaN的作用,则可能需要额外的jpjnp。但并非总是如此:例如,ja仅在CF = 0且ZF = 0时才为真,因此在无序情况下不会采用它。如果您希望无序案例采用与下面相同或相同的执行路径,则只需ja即可。


如果您要打印({。{1}}),请使用JA,如果不打印,则应使用if (!(f2 > f1)) { puts("hello"); }(对应JBE)。 (注意这可能有点令人困惑,因为如果我们不跳,我们只打印)。


关于你的第二个问题:默认情况下if (!(f2 <= f1)) { puts("hello"); }不会弹出任何内容。你想要它的近亲[{1}}弹出fcomi。您应该在使用后始终清除fpu寄存器堆栈,因此假设您希望打印消息,所有程序中的所有程序都会以此结束:

fcomip