Why use abs() or fabs() instead of conditional negation? Why use abs() or fabs() instead of conditional negation? c c

Why use abs() or fabs() instead of conditional negation?


The "conditional abs" you propose is not equivalent to std::abs (or fabs) for floating point numbers, see e.g.

#include <iostream>#include <cmath>int main () {    double d = -0.0;    double a = d < 0 ? -d : d;    std::cout << d << ' ' << a << ' ' << std::abs(d);}

output:

-0 -0 0

Given -0.0 and 0.0 represent the same real number '0', this difference may or may not matter, depending on how the result is used. However, the abs function as specified by IEEE754 mandates the signbit of the result to be 0, which would forbid the result -0.0. I personally think anything used to calculate some "absolute value" should match this behavior.

For integers, both variants will be equivalent both in runtime and behavior. (Live example)

But as std::abs (or the fitting C equivalents) are known to be correct and easier to read, you should just always prefer those.


The first thing that comes to mind is readability.

Compare these two lines of codes:

int x = something, y = something, z = something;// Compareint absall = (x > 0 ? x : -x) + (y > 0 ? y : -y) + (z > 0 ? z : -z);int absall = abs(x) + abs(y) + abs(z);


The compiler will most likely do the same thing for both at the bottom layer - at least a modern competent compiler.

However, at least for floating point, you'll end up writing a few dozen lines if you want to handle all the special cases of infinity, not-a-number (NaN), negative zero and so on.

As well as it's easier to read that abs is taking the absolute value than reading that if it's less than zero, negate it.

If the compiler is "stupid", it may well end up doing worse code for a = (a < 0)?-a:a, because it forces an if (even if it's hidden), and that could well be worse than the built-in floating point abs instruction on that processor (aside from complexity of special values)

Both Clang (6.0-pre-release) and gcc (4.9.2) generates WORSE code for the second case.

I wrote this little sample:

#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 makes this code for 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

Note that both cases are notably more complex in the second form, and in the gcc case, it uses a branch. Clang uses more instructions, but no branch. I'm not sure which is faster on which processor models, but quite clearly more instructions is rarely better.