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

前端 未结 9 728
南方客
南方客 2021-01-31 07:38

In C/C++, why should one use abs() or fabs() to find the absolute value of a variable without using the following code?

int absoluteVal         


        
相关标签:
9条回答
  • 2021-01-31 08:00

    The intent behind abs() is "(unconditionally) set the sign of this number to positive". Even if that had to be implemented as a conditional based on the current state of the number, it's probably more useful to be able to think of it as a simple "do this", rather than a more complex "if… this… that".

    0 讨论(0)
  • 2021-01-31 08:05

    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.

    0 讨论(0)
  • 2021-01-31 08:06

    ...and would you make it into a macro, you can have multiple evaluations that you may not want (side efffects). Consider:

    #define ABS(a) ((a)<0?-(a):(a))
    

    and use:

    f= 5.0;
    f=ABS(f=fmul(f,b));
    

    which would expand to

    f=((f=fmul(f,b)<0?-(f=fmul(f,b)):(f=fmul(f,b)));
    

    Function calls won't have this unintended side-effects.

    0 讨论(0)
提交回复
热议问题