x86 汇编程序:浮点比较

x86 assembler: floating point compare

提问人:JustMaximumPower 提问时间:8/14/2011 最后编辑:ecmJustMaximumPower 更新时间:3/19/2022 访问量:21161

问:

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

假设我要比较的两个值是浮点堆栈上的唯一值,那么该指令将比较这些值并设置 CPU 标志,以便 、 、 、 ...可以使用说明。fcomijejnejl

我问是因为这只有时有效。例如:

.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是只弹出一个值还是同时弹出两个值?

x86 浮点 比较 GNU-汇编程序

评论


答:

42赞 user786653 8/14/2011 #1

TL:DR:使用高于/低于条件(如无符号整数)来测试比较结果

由于各种历史原因(通过 fcom(PPro 中的新功能)匹配的 fcom/fstsw/sahf 从 FP 状态字映射到 FLAGS),FP 比较的是集 CF,而不是 OF / SF。另请参阅 http://www.ray.masmcode.com/tutorial/fpuchap7.htm

现代 SSE/SSE2 标量与 FLAGS 相比也遵循这一点,使用 [u] / .(与 SIMD 比较不同,SIMD 比较将谓词作为指令的一部分,作为即时的,因为它们只为每个元素生成一个全零/全一结果,而不是一组 FLAGS。comisssd


这些都来自英特尔 64 和 IA-32 架构软件开发人员手册的第 2 卷。

FCOMI仅设置一些这样做的标志。您的代码具有 和 .(因为它是它们加载到的堆栈),参考第 2A 卷第 3-348 页上的表格,您可以看到这种情况是“ST0 < ST(i)”,因此它将清除 ZF 和 PF 并设置 CF。 同时,在第 2A 卷第 3-544 页,您可以读到这意味着“如果更大(ZF=0 和 SF=OF)则跳空”。换句话说,它正在测试符号、溢出和零标志,但不设置符号或溢出!CMP%st(0) == 9%st(1) == 10JGFCOMI

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

+--------------------+---+---+---+
| Comparison results | Z | P | C |
+--------------------+---+---+---+
| ST0 > ST(i)        | 0 | 0 | 0 |
| ST0 < ST(i)        | 0 | 0 | 1 |
| ST0 = ST(i)        | 1 | 0 | 0 |
| unordered          | 1 | 1 | 1 |  one or both operands were NaN.
+--------------------+---+---+---+

我制作了这张小表格,以便更容易弄清楚:

+--------------+---+---+-----+------------------------------------+
| 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

如果任一(或两者)操作数为 NaN,则设置 .(FP 比较有 4 种可能的结果:、、或无序)。如果你关心你的代码对 NaN 做了什么,你可能需要一个额外的 or .但并非总是如此:例如,仅当 CF=0 且 ZF=0 时才为真,因此在无序情况下不会取。如果您希望无序情况采用与以下或相等相同的执行路径,那么您只需要这样做。fcomiZF=1 PF=1 CF=1><==jpjnpjaja


如果您希望它打印(即),您应该在这里使用(即),如果您不打印(对应于 )。(请注意,这可能有点令人困惑,因为我们只有在不跳时才会打印)。JAif (!(f2 > f1)) { puts("hello"); }JBEif (!(f2 <= f1)) { puts("hello"); }


关于你的第二个问题:默认情况下不会弹出任何东西。你想要它的近亲,它弹出.您应该始终在使用后清除 fpu 寄存器堆栈,因此总而言之,假设您希望打印消息,您的程序最终会像这样结束:fcomifcomip%st0

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

.globl main
    .type   main, @function
main:
    flds   f1
    flds   f2
    fcomip
    fstp   %st(0) # to clear stack
    ja     leb # won't jump, jbe will
    pushl  $msg
    call   printf
    addl   $4, %esp
leb:
    pushl  $0
    call   exit

评论

7赞 Ray Toal 8/15/2011
非常令人印象深刻的答案。优秀。一个小评论:的反义词是 ,不是 。jajbejb
2赞 user786653 8/15/2011
@Ray Toal:你说得很对。尽管在这种情况下它不会有任何区别,但我更改了示例,因为这样更有意义。