20

I want to perform some arithmetic in unsigned, and need to take absolute value of negative int, something like

do_some_arithmetic_in_unsigned_mode(int some_signed_value)
{
   unsigned int magnitude;
   int negative;
   if(some_signed_value<0) {
       magnitude = 0 - some_signed_value;
       negative = 1;
    } else {
       magnitude = some_signed_value;
       negative = 0;
    }
   ...snip...
}

But INT_MIN might be problematic, 0 - INT_MIN is UB if performed in signed arithmetic. What is a standard/robust/safe/efficient way to do this in C?

EDIT:

If we know we are in 2-complement, maybe implicit cast and explicit bit ops would be standard? if possible, I'd like to avoid this assumption.

do_some_arithmetic_in_unsigned_mode(int some_signed_value)
{
   unsigned int magnitude=some_signed_value;
   int negative=some_signed_value<0;
   if (negative) {
       magnitude = (~magnitude) + 1;
    }
   ...snip...
}

5 Answers 5

30

Conversion from signed to unsigned is well-defined: You get the corresponding representative modulo 2N. Therefore, the following will give you the correct absolute value of n:

int n = /* ... */;

unsigned int abs_n = n < 0 ? UINT_MAX - ((unsigned int)(n)) + 1U
                           : (unsigned int)(n);

Update: As @aka.nice suggests, we can actually replace UINT_MAX + 1U by 0U:

unsigned int abs_n = n < 0 ? -((unsigned int)(n))
                           : +((unsigned int)(n));
9
  • 5
    Oh, and in the purely theoretical case where UINT_MAX == INT_MAX == -(INT_MIN+1), it is impossible to represent |INT_MIN| as an unsigned int anyway =) Commented Sep 1, 2012 at 21:42
  • @DanielFischer: is that case actually possible, given that int and unsigned int are required to have the same size and alignment requirements?
    – Kerrek SB
    Commented Sep 1, 2012 at 22:03
  • 1
    It is possible, unsigned int could have one more padding bit than int. I've never heard of an implementation where that's the case, but the standard doesn't guarantee that it never happens. (Unless I've overlooked something.) Commented Sep 1, 2012 at 22:06
  • 1
    If I write abs_n = n<0 ? 0U - ((unsigned int)(n)) : ((unsigned int)(n)); is it equally well defined?
    – aka.nice
    Commented Sep 2, 2012 at 10:15
  • 1
    Actually, my proposed test is wrong. It accounts for 2's complement implementations, but not 1s' complement or sign-magnitude, where (unsigned)INT_MIN != 0 would be true even if the absolute value didn't fit. The "Fischer condition" is necessary and sufficient if you want your function to return unsigned int, but a correct test for it isn't quite that simple... Commented Sep 3, 2012 at 9:53
6

In the negative case, take some_signed_value+1. Negate it (this is safe because it can't be INT_MIN). Convert to unsigned. Then add one;

4
  • I checked and gcc generate same code for 1U+(unsigned)(-(x+1)) and for -(unsigned)(x), something like (~magnitude)+1 but branchless, so both will be as efficient. Later one seems a bit less intention obscuring though.
    – aka.nice
    Commented Sep 2, 2012 at 19:34
  • @aka.nice: Yes, I just checked it too. 1+(unsigned)-(x+1) is perhaps a bit obscure, but it does not bring in the value conversion of a negative signed quantity to unsigned; the cast is purely a change in type, not a change in value. In your version, some reasoning effort needs to go into assuring that the arithmetic does what's expected; the argument is not as simple as "the values are in a safe range at each step". Commented Sep 2, 2012 at 19:39
  • 1
    Yes, indeed your solution better fit my initial intention. Re-interpreting the negative x as a positive is intention obscuring for one closely reading the code, and require knowledge of standard conventions. But less attentive reader will immediately recognize a form of abs in -(unsigned)x...
    – aka.nice
    Commented Sep 3, 2012 at 18:08
  • Suggest adding code this this good answer: something like unsigned abs_u(int i) { return i < 0 ? -(i+1) + 1u : (unsigned) i; }. Commented Apr 9, 2015 at 16:31
2

You can always test for >= -INT_MAX, this is always well defined. The only case is interesting for you is if INT_MIN < -INT_MAX and that some_signed_value == INT_MIN. You'd have to test that case separately.

2

I want to perform some arithmetic in unsigned, and need to take absolute value of negative int, ...

To handle pedantic cases:

The |SOME_INT_MIN|1 has some special cases:

1. Non-two's complement

Ones' complement and sign-magnitude are rarely seen these days.

SOME_INT_MIN == -SOME_INT_MAX and some_abs(some_int) is well defined. This is the easy case.

#if INT_MIN == -INT_MAX
some_abs(x);  // use matching abs, labs, llabs, imaxabs
#endif

2. SOME_INT_MAX == SOME_UINT_MAX, 2's complement

C allows the max of the signed and unsigned version of an integer type to be the same. This is rarely seen these days.

2 approaches:
1) use a wider integer type, if it exist.

#if -INTMAX_MAX <= SOME_INT_MIN
imaxabs((intmax_t)x)
#endif

2) Use wide(st) floating-point (FP) type.
Conversion to a wide FP will work for SOME_INT_MIN (2's complement) as that value is a -(power-of-2). For other large negatives, the cast may lose precision for a wide integer and not so wide long double. E.g. 64-bit long long and 64-bit long double.

fabsl(x);  // see restriction above.

3. SOME_INT_MAX < SOME_UINT_MAX

This is the common case well handle by @Kerrek SB's answer. The below also handles case 1.

x < 0 ? -((unsigned) x) : ((unsigned) x);

Higher Level Alternative

In cases when code is doing .... + abs(x), a well defined alternative is to subtract the negative absolute value: .... - nabs(x). Or as in abs(x) < 100, use nabs > -100.

// This is always well defined.
int nabs(int x) {
  return (x < 0) x : -x;
}

1 SOME_INT implies int, long, long long or intmax_t.

0
 static unsigned absolute(int x)
 {
          if (INT_MIN == x) {
                  /* Avoid tricky arithmetic overflow possibilities */
                  return ((unsigned) -(INT_MIN + 1)) + 1U;
          } else if (x < 0) {
                  return -x;
          } else {
                  return x;
          }
 }
1
  • It sounds OK, but it's mostly @R answer with one more branch, or maybe just @Jens answer...
    – aka.nice
    Commented Jul 6, 2015 at 20:44

Not the answer you're looking for? Browse other questions tagged or ask your own question.