浮点数比较技巧:内联装配

Floating point number comparison trick: inline assembly

本文关键字:比较 浮点数      更新时间:2023-10-16

很久以前,由于浮点数比较,我使用了这个简单的X86汇编技巧来获得0或1

fld [value1]
fcom [value2]
fnstsw ax
mov al, ah
and eax, 1

如果比较结果仅影响从2个值组中的值选择,则此技巧允许避免分支。在奔腾时期里这是快速的,现在可能并不快,但是谁知道。

现在,我主要使用C 并使用Intel C 编译器或GCC C 编译器进行编译。

有人可以帮助将此代码重写为2个内置汇编器口味(Intel和GCC)。

所需的功能原型是:Inline int比较了索引(const double value1,const double value2){...}

也许使用SSE2操作可能更有效。您的观点?


我已经尝试过:

__asm__(
    "fcomq %2, %0n"
    "fnstsw %axn"
    "fsubq %2, %0n"
    "andq $L80, %eaxn"
    "shrq $5, %eaxn"
    "fmulq (%3,%eax), %0n"
    : "=f" (penv)
    : "0" (penv), "F" (env), "r" (c)
    : "eax" );

但我在英特尔C 编译器中遇到了错误:浮点输出约束必须指定一个寄存器。

正如您提到的,自五个时代以来情况发生了变化:

  • SSE现在是浮点而不是X87的首选指令集,即使是标量操作
  • 优化编译器现在非常好

因此,首先检查编译器生成的内容,您可能会感到惊喜。我在以下代码上尝试了使用-O3的G

fcmp.cpp:

int compareDoublesIndexed( const double value1, const double value2 ) {
    return value1 < value2 ? 1 : 0;
}

这是编译器生成的

0000000000400690 <_Z21compareDoublesIndexeddd>:
  400690:       31 c0                   xor    %eax,%eax
  400692:       66 0f 2e c8             ucomisd %xmm0,%xmm1
  400696:       0f 97 c0                seta   %al
  400699:       c3                      retq   

这就是它的含义

  xor     %eax,%eax        ; EAX = 0
  ucomisd %xmm0,%xmm1      ; compare value2 (in %xmm1) with value1 (in %xmm0)
  seta    %al              ; AL = value2 > value1 ? 1 : 0

因此,编译器使用seta指令避免了条件分支(如果结果为" 1",则将字节设置为" 1",否则为'0')。