55

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

int absoluteValue = value < 0 ? -value : value;

Does it have something to do with fewer instructions at lower level?

Xam
  • 130
  • 2
  • 5
  • 13
Subhranil
  • 821
  • 1
  • 8
  • 21
  • 25
    `c/c++` why? why do you use both like this? – user2736738 Feb 04 '18 at 14:18
  • 24
    Better readability and compiler optimization. – iBug Feb 04 '18 at 14:18
  • 10
    abs() is a pre-defined and well-known function, so almost all programmers use it. This lead to uniformity in codes. – H.H Feb 04 '18 at 14:20
  • 1
    Disassemble both ways, see if they are different. C++ `std::abs`, or the various `abs` family in C may do the same thing as the snippet, or may be (hopefully is) more efficient. – Eljay Feb 04 '18 at 14:22
  • 31
    Why reinvent something that's been tested and added to standard library, especially when you might get it wrong or miss special cases? – codebender Feb 04 '18 at 14:50
  • 4
    What does `abs` do if `value` has the minimum value for your signed type? `-value` would result in overflow in that case. – chepner Feb 04 '18 at 15:49
  • 8
    @chepner *"The behavior is undefined if the result cannot be represented by the return type."* From the [doc](http://en.cppreference.com/w/cpp/numeric/math/abs). – Baum mit Augen Feb 04 '18 at 15:51
  • 1
    @Alexander: Getting off topic, but the answer is that C exists near the minimum of the complexity curve: using either assembly or C++ is very likely to make your code more complicated. The same applies to using functions for math operations: when the algorithm you're implementing is written as |x|, then abs (x) is a simple translation. – jamesqf Feb 04 '18 at 19:08
  • 5
    Archaic case note: For any sign-magnitude/one's compliment machines out there: It does not appear `abs(-0)` _must_ return +0 per spec. I wonder what such machines did. – chux - Reinstate Monica Feb 04 '18 at 20:02
  • 11
    Why *wouldn't* you use abs or fabs? – user253751 Feb 04 '18 at 22:44
  • 1
    @chux That would be an interesting question for [retrocomputing.se], but don't expect it to be answered in the comments here. Also, I think it's UB. – wizzwizz4 Feb 04 '18 at 23:20
  • 4
    Well, at least this is better than `sqrt(value*value)`, a sin I've also seen more than once... – leftaroundabout Feb 05 '18 at 01:10
  • @immibis As `abs(INT_MIN)` is UB, code may need to consider [alternatives](https://stackoverflow.com/a/48612366/2410359) as able. – chux - Reinstate Monica Feb 05 '18 at 03:37
  • 1
    WET: "We Enjoy Typing", aka "Waste Everyone's Time" https://en.wikipedia.org/wiki/Don%27t_repeat_yourself – Daniel R. Collins Feb 05 '18 at 18:01
  • 3
    @leftaroundabout right; one may just as well ask _'Why use `abs()` instead of `sqrt( pow(value, 2) )`'?_... like _'Why write what we mean using the simple tools provided when we could instead create clutter by writing the details ourselves?'_ Things like this recur so often that it's good to have the library provide them as single calls. My question would be why **not** use the convenience function. The other example that jumps to mind is the annoyance of having manually to clamp numbers between minima and maxima - something C++ has only just started to provide in the stdlib, much to my relief – underscore_d Feb 05 '18 at 19:52
  • I'd assume the conditional + negation is much slower than a built-in CPU instruction such as "FABS". – Mark K Cowan Feb 05 '18 at 20:30

9 Answers9

121

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.

Pharap
  • 3,161
  • 4
  • 31
  • 44
Baum mit Augen
  • 46,177
  • 22
  • 136
  • 173
  • 18
    "Negative zero" in floating point is really something to take into account. – iBug Feb 04 '18 at 14:32
  • Same will be with a NaN with sign bit of `1`, like `-std::numeric_limits::quiet_NaN()`. – Ruslan Feb 04 '18 at 19:11
  • 6
    This answer is informative. However, for a function like `abs()`, even standard implement is not perfect. `INT_MIN < 0 && abs(INT_MIN) < 0` is true. – llllllllll Feb 04 '18 at 19:22
  • 16
    @liliscent `abs(INT_MIN)` is UB even, at least in C++, but that's kind of a fundamental limitation of the language. You want to fix the return type to match the source type (as specified in IEEE754, for example), so the runtime problem of the input being `INT_MIN` can't really be worked around anyways. – Baum mit Augen Feb 04 '18 at 19:27
  • 2
    @BaummitAugen You're right, even in C99 it's also UB http://port70.net/~nsz/c/c99/n1256.html#7.20.6.1p2. – llllllllll Feb 04 '18 at 19:35
  • 1
    I don't know if this really can happen, but if you divide 1 by 0 and get infinity, you might get -infinity when dividing 1 by -.0. – Thern Feb 05 '18 at 09:08
  • 1
    @Nebr This is indeed one of the cases where it makes a difference. https://wandbox.org/permlink/37Xk8h7qSJbHqFxl – Baum mit Augen Feb 05 '18 at 13:43
  • @BaummitAugen Thanks for trying it out. – Thern Feb 05 '18 at 14:10
  • This is a good example of why you should never write your own code when a good off-the-shelf library is available. It's a safe bet that the library version was written by somebody with more domain knowledge than you. It will also have been exposed to more eyeballs looking for edge cases like this one. – Isaac Rabinovitch Feb 08 '18 at 22:58
86

The first thing that comes to mind is readability.

Compare these two lines of codes:

int x = something, y = something, z = something;
// Compare
int absall = (x > 0 ? x : -x) + (y > 0 ? y : -y) + (z > 0 ? z : -z);
int absall = abs(x) + abs(y) + abs(z);
iBug
  • 30,581
  • 7
  • 64
  • 105
  • 13
    Whoa, with all due respect, but that's a lot of up votes for a low-ball nitpick. The question clearly isn't about syntax but rather implementation. Your "problem" is trivial to solve... And your answer is good for question "why should we wrap one-liners into functions?" – luk32 Feb 04 '18 at 17:45
  • 78
    @luk32 The question was "why choose one thing over another?" One answer is readability, because they do differ quite a lot. I wouldn't call this a "nitpick". – SH7890 Feb 04 '18 at 21:21
  • 4
    @SH7890 Readability is a no issue, because you can write a function to prevent this, or heck even a macro. It takes one line, and will look exactly the same. Here's a fix for readability: `int cabs(int a) {return a > 0 ? a : -a;}`. There even is a hint, that it's about implementation. No one sane will copy-paste whole implementation on each use case. Come on. – luk32 Feb 04 '18 at 22:08
  • 44
    @luk32 Sure, but the standard library authors helpfully foresaw you might want to do this and so they wrote that function for you so you don't need to write it yourself. It's called `abs`. Why bother to write your own `cabs` function that does exactly the same thing? One reason might be that you don't know `abs` exists, but since you do now, it's not like you get anything out of spiting it. – user253751 Feb 04 '18 at 22:39
  • 2
    @immibis "Why bother to write your own cabs function that does exactly the same thing?" - I don't know but that is OP's question as I understand it. What is the difference between `a > 0 ? a : -a` and standard `abs()`... and it is not readability. All your points are valid. I just think calling in readability (which you didn't) is ludicrous. Anyways, this is a popular vote, and I respect that. I've expressed my opinion. Virtual beer for everyone. – luk32 Feb 04 '18 at 23:45
  • 4
    @luk32 you got my point. Readability is certaily not a concern since I can define macros/functions with that line of code. I was looking for lower level equivalence or dissymmetry. – Subhranil Feb 05 '18 at 02:21
  • 1
    @luk32 it's not a lot of upvotes, since it answers the question - I think the voting stats are interesting because what it's doing is demonstrating how much programmers care about readability. – moopet Feb 06 '18 at 09:49
  • 3
    @moopet For the last time. To me it shows, how many programmers miss the point and can fixate on something irrelevant, just to find an issue. This doesn't answer the question. It answers a different question. I agree with every point of immibis, I wouldn't reimplement `abs`, readability is very important to me... but it is not an issue in terms of this question. Apparently to most I am wrong, I accept that. I prefer people to be oversensitive to readability, rather than oblivious, so I am fine with it. Cheers. – luk32 Feb 06 '18 at 12:02
  • 1
    @Subhranil A function maybe, but **not** a macro. Making `abs` a macro is just asking for trouble. Consider what the result of `abs(++i)` would be if `abs` were a macro. – Pharap May 09 '19 at 13:21
30

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.

Mats Petersson
  • 119,687
  • 13
  • 121
  • 204
  • 23
    This answer says that a modern competent compiler will most likely do the same thing for both then shows assembly code demonstrating that the selected compilers did not do the same thing. That is a conflicting, confusing message. Are the selected compilers incompetent or unmodern? Why use them for examples then? Or was the statement that a modern competent compiler will most likely do the same thing for both incorrect? Why is there a difference in the generated code? – Eric Postpischil Feb 04 '18 at 16:54
  • 10
    "*quite clearly more instructions is rarely better.*" I beg to differ, especially when you compare against branching. It trips prefetching and out-of-order execution. It's complex and probably isn't safe to be generalized. – luk32 Feb 04 '18 at 17:43
  • 1
    Is this at `-O3`? – Calchas Feb 05 '18 at 10:28
  • @EricPostpischil, for the integer case, `clang` produced identical code for both. `gcc`'s code differs slightly in that the `abs()` version computes and stores the result in a single operation, while the conditional version computes the result into a register, then copies it to memory. – Mark Feb 06 '18 at 00:10
  • @EricPostpischil: I wrote the answer FIRST, then tried it, and found that it's nearly the same, but not quite identical - in particular, the last sentence before "and look what the compiler actually does" - the conditional code actually does a branch in the gcc case, which is avoided in the `abs` code. – Mats Petersson Feb 07 '18 at 00:23
  • @luk32: Agreed, but both gcc and clang does branchless code in the abs-code, but in the conditional code, both compilers generate more instructions, gcc also adds a conditional branch in the mix. So, instruction count AND type of instruction is worse. – Mats Petersson Feb 07 '18 at 00:38
  • 2
    @Calchas: I used -O2, which is what I normally use for "optimized" code. But -O3 didn't alter the code in any noticeable way. A newer version of gcc may be different - but I don't have one of those right now - my computer is about to be upgraded, so will get a new compiler as well [it wasn't starting this morning, got it going this evening, replacement bits on the way]. – Mats Petersson Feb 07 '18 at 00:39
  • @EricPostpischil the compilers may be old, but some system still uses those. Take RHEL7, which, by default, comes with a 4.8.x gcc (yes you can get developer toolsets with newer gcc, but still). For those people who do little coding, and are not interested in getting a compiler different from what the system has, this example can point out an interesting behavior – bartgol Apr 06 '20 at 20:18
  • @bartgol: The existence or nonexistence of such compilers is not pertinent to my comment, which is about the conflicting and confusing statements in the answer, not about what compilers exist or what compilers are currently in use. – Eric Postpischil Apr 07 '20 at 00:53
13

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

Various reasons have already been stated, yet consider conditional code advantages as abs(INT_MIN) should be avoided.


There is a good reason to use the conditional code in lieu of abs() when the negative absolute value of an integer is sought

// Negative absolute value

int nabs(int value) {
  return -abs(value);  // abs(INT_MIN) is undefined behavior.
}

int nabs(int value) {
  return value < 0 ? value : -value; // well defined for all `int`
}

When a positive absolute function is needed and value == INT_MIN is a real possibility, abs(), for all its clarity and speed fails a corner case. Various alternatives

unsigned absoluteValue = value < 0 ? (0u - value) : (0u + value);
chux - Reinstate Monica
  • 113,725
  • 11
  • 107
  • 213
  • 3
    +1 for abs(INT_MIN) is undefined behavior. I didn't knew that. Why the library implementers made it undefined? – manav m-n Feb 05 '18 at 05:25
  • 3
    @manavm-n Consider that possible results are `abs(INT_MIN) --> INT_MIN`, or `abs(INT_MIN) --> INT_MAX` or program dies or etc.. None are universally preferred, so best to allow an implementation to be fast with other values and let `abs(INT_MIN) --> UB` to embrace all implementations. [Agree to disagree](https://en.wikipedia.org/wiki/Agree_to_disagree) – chux - Reinstate Monica Feb 05 '18 at 05:30
  • @manavm-n Note: I only use `abs(x)` when `x` cannot be `INT_MIN`, else I code otherwise to avoid UB. – chux - Reinstate Monica Feb 05 '18 at 05:34
  • I agree to disagree with your assertion that it is undefined for all architectures. It would be more correct to say that it is implementation defined by the ISA of the processor. Most of them like x86 and arm have well defined assembly instructions for abs(). The results of abs() may vary on different ISA models. – manav m-n Feb 05 '18 at 05:41
  • 6
    @manavm-n The C spec specs it is UB. "If the result cannot be represented, the behavior is undefined." §7.22.6.1 2 – chux - Reinstate Monica Feb 05 '18 at 14:11
  • 1
    @chux, `abs(INT_MIN)` is only undefined behavior under two's-compliment arithmetic. Under sign-magnitude arithmetic (not that anybody uses it any more), it's perfectly well defined. – Mark Feb 06 '18 at 00:13
  • Testing your code `unsigned absoluteValue = value < 0 ? (0u - INT_MIN) : (0u + INT_MIN);`, I get a value equal to INT_MIN under both gcc and clang. I presume that 0u has been auto-converted to signed for the `(0u - INT_MIN)` operation; UB again. – Trevor Powell Feb 06 '18 at 04:31
  • 1
    @TrevorPowell: You presume wrongly, `(0u - value)` is performed using promotion to `unsigned int` and the result is computed modulo, like all unsigned arithmetic. – Ben Voigt Feb 06 '18 at 06:06
  • @TrevorPowell Concerning [I get a value equal to INT_MIN under both gcc and clang](https://stackoverflow.com/users/777318/trevor-powell), how did you determine that an `unsigned` variable is a negative number like `INT_MIN`? Did you print it with `"%d"`? – chux - Reinstate Monica Feb 06 '18 at 06:09
  • @Mark Agree with rare platforms that use non-2's complement, `abs(x)` is not an issue. – chux - Reinstate Monica Feb 06 '18 at 06:11
7

There might be a more-efficient low-level implementation than a conditional branch, on a given architecture. For example, the CPU might have an abs instruction, or a way to extract the sign bit without the overhead of a branch. Supposing an arithmetic right shift can fill a register r with -1 if the number is negative, or 0 if positive, abs x could become (x+r)^r (and seeing Mats Petersson's answer, g++ actually does this on x86).

Other answers have gone over the situation for IEEE floating-point.

Trying to tell the compiler to perform a conditional branch instead of trusting the library is probably premature optimization.

Davislor
  • 12,287
  • 2
  • 26
  • 36
4

Consider that you could feed a complicated expression into abs(). If you code it with expr > 0 ? expr : -expr, you have to repeat the whole expression three times, and it will be evaluated two times.
In addition, the two result (before and after the colon) might turn out to be of different types (like signed int / unsigned int), which disables the use in a return statement. Of course, you could add a temporary variable , but that solves only parts of it, and is not better in any way either.

Aganju
  • 5,994
  • 1
  • 9
  • 22
  • This is easily worked around by initializing a temporary variable. An argument with side-effects would give incorrect results if evaluated twice! `abs(printf("hello, world!\n"))`? – Davislor Feb 04 '18 at 18:50
  • The question do specify the absolute value of _a variable_ though, so side-effects are not relevant to this particular question. – pipe Feb 05 '18 at 12:03
  • It will be evaluated twice, not three times. `a ? b : c` only evaluates one of `b` and `c`. – L. F. Oct 05 '19 at 06:10
4

...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.

Paul Ogilvie
  • 24,146
  • 4
  • 18
  • 39
  • 1
    True, but basically stated already by https://stackoverflow.com/a/48611734/2757035. I'm also curious who said anything about macros in the first place. :P – underscore_d Feb 06 '18 at 11:54
3

Assuming that the compiler won't be able to determine that both abs() and conditional negation are attempting to achieve the same goal, conditional negation compiles to a compare instruction, a conditional jump instruction, and a move instruction, whereas abs() either compiles to an actual absolute value instruction, in instruction sets that support such a thing, or a bitwise and that keeps everthing the same, except for the sign bit. Every instruction above is typically 1 cycle, so using abs() is likely to be at least as fast, or faster than conditional negation (since the compiler might still recognize that you are attempting to calculate an absolute value when using the conditional negation, and generate an absolute value instruction anyway). Even if there is no change in the compiled code, abs() is still more readable than conditional negation.

chue x
  • 17,865
  • 6
  • 52
  • 67
Cpp plus 1
  • 930
  • 5
  • 22
3

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".

StarWeaver
  • 139
  • 3