编译器很可能会在底层为两者做同样的事情——至少是现代的称职的编译器。
但是,至少对于浮点数,如果要处理无穷大、非数字 (NaN)、负零等所有特殊情况,您最终会写几十行代码。
abs 取绝对值比读取它小于零的值更容易阅读。
如果编译器是“愚蠢的”,它最终可能会为a = (a < 0)?-a:a 编写更糟糕的代码,因为它会强制使用if(即使它是隐藏的),而且这很可能比内置的浮动代码更糟糕在该处理器上点 abs 指令(除了特殊值的复杂性)
Clang (6.0-pre-release) 和 gcc (4.9.2) 都会为第二种情况生成 WORSE 代码。
我写了这个小样本:
#include <cmath>
#include <cstdlib>
extern int intval;
extern float floatval;
void func1()
{
int a = std::abs(intval);
float f = std::abs(floatval);
intval = a;
floatval = f;
}
void func2()
{
int a = intval < 0?-intval:intval;
float f = floatval < 0?-floatval:floatval;
intval = a;
floatval = f;
}
clang 为 func1 编写此代码:
_Z5func1v: # @_Z5func1v
movl intval(%rip), %eax
movl %eax, %ecx
negl %ecx
cmovll %eax, %ecx
movss floatval(%rip), %xmm0 # xmm0 = mem[0],zero,zero,zero
andps .LCPI0_0(%rip), %xmm0
movl %ecx, intval(%rip)
movss %xmm0, floatval(%rip)
retq
_Z5func2v: # @_Z5func2v
movl intval(%rip), %eax
movl %eax, %ecx
negl %ecx
cmovll %eax, %ecx
movss floatval(%rip), %xmm0
movaps .LCPI1_0(%rip), %xmm1
xorps %xmm0, %xmm1
xorps %xmm2, %xmm2
movaps %xmm0, %xmm3
cmpltss %xmm2, %xmm3
movaps %xmm3, %xmm2
andnps %xmm0, %xmm2
andps %xmm1, %xmm3
orps %xmm2, %xmm3
movl %ecx, intval(%rip)
movss %xmm3, floatval(%rip)
retq
g++ func1:
_Z5func1v:
movss .LC0(%rip), %xmm1
movl intval(%rip), %eax
movss floatval(%rip), %xmm0
andps %xmm1, %xmm0
sarl $31, %eax
xorl %eax, intval(%rip)
subl %eax, intval(%rip)
movss %xmm0, floatval(%rip)
ret
g++ func2:
_Z5func2v:
movl intval(%rip), %eax
movl intval(%rip), %edx
pxor %xmm1, %xmm1
movss floatval(%rip), %xmm0
sarl $31, %eax
xorl %eax, %edx
subl %eax, %edx
ucomiss %xmm0, %xmm1
jbe .L3
movss .LC3(%rip), %xmm1
xorps %xmm1, %xmm0
.L3:
movl %edx, intval(%rip)
movss %xmm0, floatval(%rip)
ret
请注意,在第二种形式中,这两种情况都明显更复杂,而在 gcc 的情况下,它使用了一个分支。 Clang 使用更多指令,但没有分支。我不确定哪个处理器型号更快,但很明显更多的指令很少更好。