Computing the absolute value of an integer

Mark Brader msb at sq.sq.com
Sat May 5 11:19:45 AEST 1990


[Whitespace in the examples has been compressed for posting purposes.]

?  I want a function that will compute the absolute value of any integer
?  that can be stored in a variable of type int.  What makes this difficult
?  is that on some machines the absolute value of the most negative integer
?  will not fit in an int.

Which in turn means that the operation "-a" on a variable a of any
signed integer type is capable of causing overflow, which is allowed
to cause the program to abort.  You are right to be worried.

?  unsigned my_abs(int a) { if (a >= 0) return a; else return -a; }
?  Does anyone know of machines on which this will fail?

Unfortunately, in this code the negation occurs before the conversion to
unsigned, so it can fail on a machine that checks for int overflow.
(I don't know of any specific ones, but I'm assured that they exist.)
The solution is as simple as reversing the operations:  change the final
"-a" to "-(unsigned)a".

This works because all operations on unsigned integer types, including
conversion to them, are defined in modular arithmetic.  If M is 2 to the
power of the number of bits used for the unsigned type, and the value
of "a" is initially negative, then the value of (unsigned)a is M+a, 
and -(unsigned)a is M-(M+a) which is -a as desired.  (In the previous
sentence the expressions involving M are written in the notation of
mathematics, not C.)

>  What's wrong with using a macro like the following
>  #define abs(x) (((x) >= 0) ? (x) : -(x))

Use of a macro does have advantages of avoiding the need to specify
the type of its argument, and of speed in the case where its argument
is a simple variable rather than a complicated expression.  However,
it doesn't address the stated problem, which is that -x can overflow!

>  long abs(long x){
>  	return ((x >= 0) ? x: -x);
>  }
>  I made it fnc returning long and takes 1 long arg ...

The use of ?: notation versus if-else is a purely stylistic point;
some like one and some like the other.  (I like one.)  It should have
no noticeable effect on the generated code.  The use of "long", however,
does show another problem with the original example: as written, it does
not generalize to longs.  It could of course have been written for longs
in the first place.  Retaining the original style, it would then become:

   unsigned long my_labs(long a)
   { if (a >= 0) return a; else return -(unsigned long)a; }

As a macro, it could be written

   #define my_iabs(x) (((x) >= 0) ? (x) : -(unsigned long)(x))

which will work for any *integer* type, but not for floating types.
With an insufficiently clever compiler it may also generate unnecessary
widening and narrowing operations, if long is wider than int.

>  I just took a look at /usr/include/math.h and it was even more concice:
>  #define _abs(x) (x < 0 ? (-x): (x))

Concise, but wrong.  The above gives the wrong answer for _abs(z+1).
I'm glad /usr/include/math.h on the system *I* use doesn't have that!

>  In general your code looks too much like Pascal.

In general people who write only to criticize the style of someone else's
code, while missing important issues relating to the content, do not
make a useful contribution by posting to this newsgroup.

-- 
Mark Brader			    "This is Programming as a True Art Form,
SoftQuad Inc., Toronto		     where style is more important
utzoo!sq!msb, msb at sq.com	     than correctness..."     -- Pontus Hedman

This article is in the public domain.



More information about the Comp.lang.c mailing list