浮点比较 < 和 >= 生成的汇编中的严重差异

Harsh differences in generated assembly of floating-point comparisons < and >=

我正在试验生成的程序集并发现了一件有趣的事情。 有两个函数在做相同的计算。它们之间的唯一区别是结果相加的方式。

#include <cmath>

double func1(double x, double y)
{
  double result1;
  double result2;

  if (x*x < 0.0) result1 = 0.0;
  else
  {
    result1 = x*x+x+y;
  }

  if (y*y < 0.0) result2 = 0.0;
  else
  {
    result2 = y*y+y+x;
  }

  return (result1 + result2) * 40.0;
}

double func2(double x, double y)
{
  double result = 0.0;

  if (x*x >= 0.0)
  {
    result += x*x+x+y;
  }

  if (y*y >= 0.0)
  {
    result += y*y+y+x;
  }

  return result * 40.0;
}

x86 clang 3.7 生成的程序集 -O2 开关 gcc.godbolt.org 仍然非常不同和出乎意料。 (在 gcc 上编译产生类似的程序集)

    .LCPI0_0:
    .quad   4630826316843712512     # double 40
func1(double, double):                             # @func1(double, double)
    movapd  %xmm0, %xmm2
    mulsd   %xmm2, %xmm2
    addsd   %xmm0, %xmm2
    addsd   %xmm1, %xmm2
    movapd  %xmm1, %xmm3
    mulsd   %xmm3, %xmm3
    addsd   %xmm1, %xmm3
    addsd   %xmm0, %xmm3
    addsd   %xmm3, %xmm2
    mulsd   .LCPI0_0(%rip), %xmm2
    movapd  %xmm2, %xmm0
    retq

.LCPI1_0:
    .quad   4630826316843712512     # double 40
func2(double, double):                             # @func2(double, double)
    movapd  %xmm0, %xmm2
    movapd  %xmm2, %xmm4
    mulsd   %xmm4, %xmm4
    xorps   %xmm3, %xmm3
    ucomisd %xmm3, %xmm4
    xorpd   %xmm0, %xmm0
    jb  .LBB1_2
    addsd   %xmm2, %xmm4
    addsd   %xmm1, %xmm4
    xorpd   %xmm0, %xmm0
    addsd   %xmm4, %xmm0
.LBB1_2:
    movapd  %xmm1, %xmm4
    mulsd   %xmm4, %xmm4
    ucomisd %xmm3, %xmm4
    jb  .LBB1_4
    addsd   %xmm1, %xmm4
    addsd   %xmm2, %xmm4
    addsd   %xmm4, %xmm0
.LBB1_4:
    mulsd   .LCPI1_0(%rip), %xmm0
    retq

func1 编译为无分支程序集,涉及的指令比 func2 少得多。因此 func2 预计比 func1.

慢得多

有人可以解释这种行为吗?

无论您的 doubleNaN 还是 NaN,比较运算符 <>= 出现这种行为的原因都不同。其中一个操作数是 NaN return false 的所有比较。因此,无论 x 是否为 NaN,您的 x*x < 0.0 始终 为假。所以编译器可以安全地优化它。但是,x * x >= 0 的情况对于 NaN 和非 NaN 值的行为不同,因此编译器将条件跳转留在程序集中。

这就是 cppreference 关于与涉及的 NaN 进行比较的说法:

the values of the operands after conversion are compared in the usual mathematical sense (except that positive and negative zeroes compare equal and any comparison involving a NaN value returns zero)