tags:

views:

251

answers:

2

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.

A: 

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.

Mark Santesson
lol, I like this one.
Ed Swangren
If the `abs` function returned `unsigned`, having `abs(INT_MIN)` be defined would not be a problem - it wouldn't make calculating it any slower - the standard underlying negation operation would still work fine. That's not the issue - the real issue is type promotion.
caf
It shouldn't be any slower -- the common assembly sequence for absolute value is ((x + y) ^ y), which works fine for the maximum negative value resulting in an unsigned word.
cdleary
My point is, your only downside is if you are at INT_MIN, and that would be really rare. My sentence beginning with "Personally," hints at the "type" explanation provided by caf. The main thrust of my answer was to try to convince people that they hadn't lost much.
Mark Santesson
+9  A: 

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.

caf
+1 Good point, the promotion from signed to unsigned is a very strong argument :)
AraK
I can buy this -- type promotion is complex and you can't blame anyone for wishing everything fit in a signed value. I guess if I could go back in time to argue with the committee I would say that abs should be as accurate as possible and that C programmers already have to know how to deal with function return types and type promotion in expressions, but I left my time machine on a bus one time.
cdleary
Well, the argument the other way is that you need to check for underflow / overflow if you're doing arithmetic on signed values already - this just means that you need to check that an operation won't produce a result below `-INT_MAX` instead of `INT_MIN`, if you're going to pass it to `abs()`.
caf
Agreed that's the general case counter-argument. Just a nitpick clarification: you never have to check whether something is below `INT_MIN` if it's a flavor of `int`, so in this particular case of `unsigned abs(signed)` you would not have to check the argument at all before invocation.
cdleary
That's why I didn't say that you'd check if a value was below `INT_MIN` - rather you need to check that the *result* of a calcuation wouldn't be (because that would cause signed overflow, which is undefined behaviour). Eg if you're calculating `a - b`, in the normal case you need to verify `a >= INT_MIN + b` - if you're going to pass it to `abs()`, you need to change that to `a > INT_MIN + b`.
caf