[comp.lang.c] Computing the absolute value of an integer

ka@cs.washington.edu (Kenneth Almquist) (05/03/90)

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.  The following solution works on all machines I
know of:

	unsigned
	my_abs(int a) {
	      if (a >= 0)
		    return a;
	      else
		    return -a;
	}

Does anyone know of machines on which this will fail?  Can anyone suggest
a more portable implementation?
					Kenneth Almquist

c60c-3cf@e260-3c.berkeley.edu (Dan Kogai) (05/04/90)

In article <11679@june.cs.washington.edu> ka@cs.washington.edu (Kenneth Almquist) writes:
>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.  The following solution works on all machines I
>know of:
>
>	unsigned
>	my_abs(int a) {
>	      if (a >= 0)
>		    return a;
>	      else
>		    return -a;
>	}
>
>Does anyone know of machines on which this will fail?  Can anyone suggest
>a more portable implementation?
>					Kenneth Almquist

What's wrong with using a macro like the following

#define abs(x) (((x) >= 0) ? (x) : -(x))

	It should cause no problem if x is char|int|long and signed and abs(x)
itself is unneccesary if the type is unsigned.  And by using a macro
you save one jump and makes your code faster.  Even though you need to
stay away from macro your code could be as simple as

long abs(long x){
	return ((x >= 0) ? x: -x);
}
	I made it fnc returning long and takes 1 long arg because C has a 
copability of automatically aligning char|int|long appropriately.  You must
be careful though, when the conversion is from larger type to smaller
such as long to char.  Usually upper bits are truncated but sign extention
may cause some problem.  
	Remember it doesn't work in general if the variable is double|float
use fabs(x) if it's floating point variable.  But I think it's a macro, too,
that looks like

#define fabs(x) (double)(((double)(x) >= 0.0 ? (x) : (-x))

	I just took a look at /usr/include/math.h and it was even more
concice:

#define _abs(x) (x < 0 ? (-x): (x))

	In general your code looks too much like Pascal.  It's C and when in
C you'd better off program like C.  Happy coding!

---
##################  Dan The "I grok therefore I am God" Man
+ ____  __  __   +  (Aka Dan Kogai)
+     ||__||__|  +  E-mail:     dankg@ocf.berkeley.edu
+ ____| ______   +  Voice:      415-549-6111
+ |     |__|__|  +  USnail:     1730 Laloma Berkeley, CA 94709
+ |___  |__|__|  +              U.S.A
+     |____|____ +  Disclaimer: I'd rather be blackmailed for my long .sig
+   \_|    |     +              than give up my cool name in Kanji. And my
+ <- THE MAN  -> +              citizenship of People's Republic o' Berkeley
##################              has nothing 2 do w/ whatever I post, ThanQ.

msb@sq.sq.com (Mark Brader) (05/05/90)

[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@sq.com	     than correctness..."     -- Pontus Hedman

This article is in the public domain.

dsrekml@prism.gatech.EDU (Mike Mitten) (05/06/90)

In article <1990May4.121950.22726@agate.berkeley.edu> c60c-3cf@e260-3c.berkeley.edu (Dan Kogai) writes:
>What's wrong with using a macro like the following
>
>#define abs(x) (((x) >= 0) ? (x) : -(x))

...

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


Won't both of these macros blow up if x is a function such as:

	y = abs(foo(bar));

or

	y = _abs(foo(bar));


	Shalom,

	      -Mike
Mike  Mitten                              -- Irony is the spice of life. --
WREK Radio, Georgia Tech, Atlanta GA, 30332  (404) 894-2468
ARPA:  dsrekml@prism.gatech.edu
uucp: ...!{allegra,amd,hplabs,seismo,ut-ngp}!gatech!prism!dsrekml
| CAUTION:  The above is output of experimental software simulating the       |
|           result of 1000 monkeys typing on 1000 typewriters for 1000 years. |

oesterle@wpi.wpi.edu (Shawn H. Oesterle) (05/06/90)

In article <8977@hydra.gatech.EDU> dsrekml@prism.gatech.EDU (Mike Mitten) writes:
>In article <1990May4.121950.22726@agate.berkeley.edu> c60c-3cf@e260-3c.berkeley.edu (Dan Kogai) writes:
>>What's wrong with using a macro like the following
>>
>>#define abs(x) (((x) >= 0) ? (x) : -(x))

>Won't both of these macros blow up if x is a function such as:
>
>	y = abs(foo(bar));
>
>or
>
>	y = _abs(foo(bar));
>

That's right.  That is one thing which I dislike about macros in C.  For
example, take the following function which finds the height of a binary
tree.   
 
 
#define max(a,b)    (((a) > (b)) ? (a) : (b))

/* This function returns the height in O(n) time (n is the number of nodes) */

int TreeHeight(const TREE * tree)  {
     if(tree != NULL)  {
          int  x = TreeHeight(tree->left),
               y = TreeHeight(tree->right);
 
          return(1 + max(x,y));
     }
     else  {
          return(-1);
     }
}
 

/* because the 'max' macro has functions as its arguments, TreeHeight() is
   changed from a linear time complexity function to an exponential time
   complexity function! */

int TreeHeight(const TREE * tree)  {
     if(tree != NULL)  {
          return(1 + max(TreeHeight(tree->left), TreeHeight(tree->right)));
     }
     else  {
          return(-1);
     }
}

BTW, C++ resolves this problem with the 'inline' keyword for function
declarations.

-- 
Shawn Oesterle { oesterle@wpi.wpi.edu } antherea

"...I tend to think that there are only two kinds of people in 
the world anyway: those that oversimplify and those that don't."  -Jim Northrup

bph@buengc.BU.EDU (Blair P. Houghton) (05/07/90)

In article <12710@wpi.wpi.edu> oesterle@wpi.wpi.edu (Shawn H. Oesterle) writes:
>In article <8977@hydra.gatech.EDU> dsrekml@prism.gatech.EDU (Mike Mitten)
>writes:
>>In article <1990May4.121950.22726@agate.berkeley.edu>
>>c60c-3cf@e260-3c.berkeley.edu (Dan Kogai) writes:
>>>What's wrong with using a macro like the following
>>>#define abs(x) (((x) >= 0) ? (x) : -(x))
>>Won't both of these macros blow up if x is a function such as:
>>	y = abs(foo(bar));
>
>That's right.  That is one thing which I dislike about macros in C.

Macros are a textual facility, not a functional one.

				--Blair
				  "If you want the assembler, you know
				   where to find it. (cf. D. Ritchie.;
				   or was it B. Kernighan?)"

mcdaniel@amara.uucp (Tim McDaniel) (05/08/90)

c60c-3cf@e260-3c.berkeley.edu (Dan Kogai) writes:
   >What's wrong with using a macro like the following
   >#define abs(x) (((x) >= 0) ? (x) : -(x))
   {et cetera}

One problem is the use of a macro.  Why optimize if "abs" is not in
the critical path for your program?  In many dubuggers, you can't work
with a macro.  If the argument x has a side effect, as in
   j = abs(i++);
it would be evaluated twice.  You can't take the address of a macro.

"What's [really] wrong" is in the original article by
ka@cs.washington.edu (Kenneth Almquist):
   > 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.
     ^^^^ ^^^^^^^^ ^^^^^^^ ^^^^ ^^^ ^^^ ^^ ^^ ^^^

E.g. suppose we have a two's-complement machine where ints are 2 bytes
long.  Then ints range from -32768 to 32767 inclusive.  However, note
that 32768 is NOT in the valid range, so
   int x = -32768;
   -x;
may overflow.  ANSI C does not guarantee what happens when a signed
integer value overflows.  A conforming implementation may validly send
220 volts AC at 100 amps thru your terminal upon overflow.

Kenneth continues (edited):
   > The following solution works on all machines I know of:
   >	unsigned int my_abs(int a) {
   >	      if (a >= 0)
   >		    return a;
   >	      else
   >		    return -a;
   >	}
   > Does anyone know of machines on which this will fail?

In practice, no.  To explain: this code depends on -INT_MIN ==
INT_MIN, which is true on most machines.  (Two's-complement negation
is "flip the bits and add one"; with a 2-byte int, INT_MIN is 8000
hex.  Flip (invert) the bits to get 7fff and add one, getting 8000
again, if integer overflow is ignored, and it usually is.)  ANSI C
says that INT_MIN assigned to an unsigned int on a two's-complement
machine will produce -INT_MIN.

   > Can anyone suggest a more portable implementation?

How about
   	unsigned int my_abs(int a) {
   	      if (a >= 0)
   		    return (unsigned)a;
   	      else
   		    return -(unsigned)a;
   	}
?  Casting signed to unsigned is well-defined under ANSI C, and ought
to work the same under pre-ANSI.  Ditto for "-" on an unsigned value.

The first cast is not strictly necessary, but I use two rules:
- NEVER mix signed and unsigned values, unless using explicit casts.
  (And then make sure you know what'll happen.)
- ALWAYS REMEMBER: sizeof may be unsigned!
(The second one is harder to remember.)  Failing to follow these rules
can lead to extremely subtle bugs.

--
Tim McDaniel
Applied Dynamics International, Ann Arbor, MI
Internet: mcdaniel@adi.com ((else mcdaniel%amara.uucp@mailgw.cc.umich.edu))
UUCP: {uunet,sharkey}!amara!mcdaniel

ken@cs.rochester.edu (Ken Yap) (05/09/90)

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

I came across a bug that was caused by exactly this problem. In a
boundary case, a fp division by zero happened giving negative infinity
silently (another story). This was assigned to an integer. The problem
was that the author decided to do his own conversion to a string
instead of using printf or sprintf. The code said, in effect:

	if (x < 0)
		print leading -
		x = -x
	proceed as in positive case

Problem is, -maxint is -maxint, so the program printed a minus sign
and nothing else. Since this was a converter to Postscript, this bare
minus sign caused the printer to barf, aborting the job.

Moral of story? Make up your own.