【问题标题】:x86 assembler: floating point comparex86 汇编器:浮点比较
【发布时间】:2011-10-26 19:34:23
【问题描述】:

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

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

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

.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 floating-point compare assembly gnu-assembler


    【解决方案1】:

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

    对于各种historical reasons(映射from FP status word to FLAGS via fcom / fstsw / sahffcomi(PPro 中的新功能)匹配),FP 比较集合 CF,而不是 OF / SF。另见http://www.ray.masmcode.com/tutorial/fpuchap7.htm


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

    FCOMI 只设置CMP 所做的一些标志。您的代码有%st(0) == 9%st(1) == 10。 (因为它们被加载到堆栈上),参考第 2A 卷中第 3-348 页的表格,您可以看到这是“ST0 JG 的意思是“如果更大(ZF=0 和 SF=OF)则跳短”。换句话说,它正在测试符号、溢出和零标志,但FCOMI 没有设置符号或溢出!

    根据你想跳的条件,你应该看看可能的比较结果,然后决定你想跳的时间。

    +--------------------+---+---+---+ |比较结果 | Z |磷 | C | +--------------------+---+---+---+ | ST0 > ST(i) | 0 | 0 | 0 | | ST0

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

    +--------------+---+---+------+-------- ----------------+ |测试 | Z | C | Jcc |笔记 | +--------------+---+---+------+-------- ----------------+ | ST0 = ST(i) | X | 0 | JAE |只要CF清楚我们就很好| | ST0 > ST(i) | 0 | 0 | JA | CF和ZF都必须清楚 | +--------------+---+---+------+-------- ----------------+ 图例:X:不关心,0:清除,1:设置

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

    如果fcomi 的一个(或两个)操作数是NaN,则设置ZF=1 PF=1 CF=1。 (FP 比较有 4 种可能的结果:><== 或无序)。如果您关心代码对 NaN 的作用,您可能需要额外的 jpjnp。但并非总是如此:例如,ja 仅在 CF=0 且 ZF=0 时为真,因此在无序情况下将不取。如果你想让无序的case采用与下面相同或相等的执行路径,那么ja就是你所需要的。


    如果你想打印,你应该使用JA(即if (!(f2 > f1)) { puts("hello"); }),如果你不想打印,你应该使用JBE(对应于if (!(f2 <= f1)) { puts("hello"); })。 (请注意,这可能有点令人困惑,因为我们只在不跳转时才打印)。


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

    .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
    

    【讨论】:

    • 非常令人印象深刻的答案。杰出的。一个小评论:ja 的反义词是jbe,而不是jb
    • @Ray Toal:你是绝对正确的。尽管在这种情况下没有任何区别,但我更改了示例,因为这样更有意义。
    猜你喜欢
    • 2013-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-08
    • 2020-02-28
    相关资源
    最近更新 更多