浮点比较 < 和 >= 生成的汇编中的严重差异
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
.
慢得多
有人可以解释这种行为吗?
无论您的 double
是 NaN
还是 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)
我正在试验生成的程序集并发现了一件有趣的事情。 有两个函数在做相同的计算。它们之间的唯一区别是结果相加的方式。
#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
.
有人可以解释这种行为吗?
无论您的 double
是 NaN
还是 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)