Why does stdlib.h's abs() family of functions return a signed value?
Question
The negative implication of this is noted in the man page:
NOTES Trying to take the absolute value of the most negative integer is not defined.
What's the reasoning behind this and what's the best recourse for a person who would like to avoid undefined behavior? Do I have to resort to something like:
unsigned uabs(signed val) {
return val > 0
? val
: (val == 1U << ((sizeof(val) * 8) - 1))
? -1U
: -val;
}
(Intentionally hacky to emphasize displeasure with stdlib ;-)
Example
Say you had a 4-bit signed value (for ease of understanding). unsigned max is 15, signed (positive) max is 7, signed (negative) min is -8, so abs(-8) won't fit into a signed value. Sure, you can represent it as -8, but then division and multiplication with the result don't work as expected.
Solution
The real answer to this question lies in the type promotion rules.
If I apply an arithmetic operator to an unsigned int
and an int
, then the int
argument is promoted to unsigned
, and the result is also unsigned
.
If the abs()
function returned unsigned
, then it would cause this kind of type promotion of other values when it was used in an expression, which would cause unexpected results. For example, this code:
if (abs(-1) * -1 < 0)
printf("< 0\n");
else
printf(">= 0\n");
Would print ">= 0", which many wouldn't like. The tradeoff, of not being able to use the single value INT_MIN
, presumably seemed OK.
OTHER TIPS
Why would it ever return a value using the unsigned space?
Let's consider 8 bit signed and unsigned numbers. If you have -128, the result is undefined... I guess stdlib doesn't want to slow things down that much. If you think you might have a number in that range, then you need to use something else.
If you think you might have a value greater than 127 in your signed char, then you are mistaken.
Ergo, it is unnecessary for the value to be able to hold a value greater than 127, and keeping it signed loses nothing. If you want to cast it to unsigned, go ahead. Since it just used to be a signed integer, the odds are good that you will be doing signed math again. Personally, I think I'd prefer that the type stay signed, since it is pretty rare that I actually want to be dealing with unsigned and I'm not doing bit operations.
But maybe someone else can dig up some notes from the standards committee.