生成的浮点比较装配的严重差异<和> =

时间:2015-11-05 15:01:50

标签: c++ assembly floating-point compiler-optimization

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

#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 {{3}}开启的程序集是如此不同和意外。 (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慢得多。

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

1 个答案:

答案 0 :(得分:15)

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

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

  

转换后操作数的值按通常的数学意义进行比较(除了正负零比较相等,任何涉及NaN值的比较返回零)