浮点数比较技巧:内联汇编

Posted

技术标签:

【中文标题】浮点数比较技巧:内联汇编【英文标题】:Floating point number comparison trick: inline assembly 【发布时间】:2014-03-09 07:38:24 【问题描述】:

很久以前,我使用过这个简单的 x86 汇编程序技巧来获得 0 或 1 作为浮点数比较的结果:

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

如果比较结果仅影响从一组 2 个值中选择一个值,则此技巧可以避免分支。在 Pentium 时代它很快,现在可能没有那么快,但谁知道呢。

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

有人可以帮忙把这段代码改写成 2 种内置的汇编器风格(Intel 和 GCC)。

所需的函数原型是: 内联 int compareDoublesIndexed( const double value1, const double value2 ) ...

也许使用 SSE2 操作会更有效。你的观点?


我试过了:

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

但英特尔 C++ 编译器出现错误:浮点输出约束必须指定单个寄存器。

【问题讨论】:

你有上下文吗?为什么选择 asm,你的性能限制是什么? 我需要在递归过滤器 (DSP) 中执行大量这样的 2 数比较,所以我根本不能使用 SIMD 指令。基本示例是: env += ( penv - env ) * ( penv 【参考方案1】:

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

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',否则设置为'0')。

【讨论】:

谢谢,知道这很有用,将尝试使用英特尔 C++ 编译器。 经过测试,在 SSE2 模式下,它会生成“cmple”和“cmpnle”指令,这可能更有效。 (那是 "( penv - env ) * c[ penv

以上是关于浮点数比较技巧:内联汇编的主要内容,如果未能解决你的问题,请参考以下文章

golang比较浮点数是不是相等

浮点数的比较

php浮点数比较

浮点数比较为啥没有相等的函数

浮点数的比较

浮点数的比较