[comp.lang.c] Type-independent ABS

gmaranca@cipc1.Dayton.NCR.COM (Gabriel Maranca) (10/05/89)

I am posting a few answers to my question on why isn't the abs function 
type independent. I wish to thank everybody. 

Based on the answers, my ABS macro now has become:

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

If anybody decides to use it, the macro is faster and more generic than 
the standard abs and labs functions, but invocations like ABS(k++) won't 
work properly. A complex expression may be passed as a parameter, as long
as evaluating it three times doesn't cause problems. If the expression is
very complex, or if it is a function call, the ABS macro may wind up being
slower than the abs and labs functions.

These are the answers I got:

chris@mimsy.UUCP (Chris Torek) writes:

> >#define ABS(x) (((long)(x) < 0L)? -(x) : (x))
> The cast to long and the 0L are both unnecessary.  If left out, this
> ABS will do its comparison in whatever type x has.
Thanks for illuminating me. I didn't know context type promotion was a
standard feature of C (I knew my compiler did it). This will greatly 
simplify my code (I was doing these silly casts everywhere, thinking
I was creating more portable code).

> Note that ABS(k++) and the like will do `mysterious' things, and
Thanks again. This explains why abs is not a macro.
> that on most machines, ABS(largest_negative_integer) is either
> a (compile or run)-time trap or simply largest_negative_integer.
If you have time, could you explain how is this problem resolved by
the standard library function?


flaps@dgp.toronto.edu (Alan J Rosenthal) writes:

> It is portable but will only work for ints and longs.  But the following
> is portable and will work for all types, including unsigned long, double,
> and long long if it exists:
> #define ABS(x) (((x) < 0) ? -(x) : (x))
> The zero will get promoted to the correct type.
Thanks. This is the same answer I got from Chris.


gwyn@brl.arpa (Doug Gwyn) writes:

> That's just the way it's always been.
English is not my native language, so I may have misinterpreted your
statement. If you mean the abs function has always been type independent,
my K&R book (second edition - pg. 253) makes reference to two functions: 
     int abs(int n)         and         long labs(long n) 
But maybe you meant the abs function has always been type dependent,
which doesn't seem a very good reason for keeping it the same way.

> Note that if its argument has side effects, funny behavior may occur.
Thanks. I will avoid passing such arguments. This IS a good reason for 
keeping the abs and labs functions the same.


bill davidsen (davidsen@crdos1.crd.GE.COM) writes:

>	1. instead of -(x) use (-(x)).
Good point. I had overlooked this potential trap in my original macro.

>	2. you might want to use (double) to work with more types
Per Chris and Alan, a better approach is to remove the cast altogether. 
This should work with any current or future data type. Please let me 
know if this is not true for some C compiler.

>	3. you still have to be careful about what you use for an
>	   argument to avoid things like ABS(x[m++]) which are only
>	   evaluated once if ABS is a procedure.
OK. I wonder how many standard library "functions" are implemented as 
macros that could behave similarly? Or have such macros consistently 
been avoided because of this side effect?

By the way, Bill, I am not a "she" (do not confuse Gabriel and Gabrielle) ;-)

---
#Gabriel Maranca
#Gabriel.Maranca@cipc1.Dayton.NCR.COM
#...!uunet!ncrlnk!cipc1!gmaranca
-- 
#Gabriel Maranca
#Gabriel.Maranca@cipc1.Dayton.NCR.COM
#...!uunet!ncrlnk!cipc1!gmaranca

chris@mimsy.UUCP (Chris Torek) (10/07/89)

[#define ABS(x) ((x) < 0 ? -(x) : (x))]

In an article whose referent was deleted by deficient news software,
I wrote:
>>on most machines, ABS(largest_negative_integer) is either a (compile
>>or run)-time trap or simply largest_negative_integer.

In article <1392@cipc1.Dayton.NCR.COM> gmaranca@cipc1.Dayton.NCR.COM
(Gabriel Maranca) writes:
>... A complex expression may be passed as a parameter, as long
>as evaluating it three times doesn't cause problems.

Actually, x is evaluated exactly twice.

>If you have time, could you explain how is this problem resolved by
>the standard library function?

The short answer is `not'.  It is hard to give a single `best' answer
for -(-2147483647 - 1) on a 32-bit two's complement machine.  The
library routines I have seen simply negate it and do whatever the
machine's instruction set does on `impossible negation'.

>... I wonder how many standard library "functions" are implemented as 
>macros that could behave similarly? Or have such macros consistently 
>been avoided because of this side effect?

In general, the latter is true.  The proposed ANSI C standard requires
that side effects take place exactly once, which effectively prohibits
a straightforward macro version of abs() and labs().  One can still
write, e.g.,

	int abs(int);
	#define	abs(x)	__abs(x)
	static __inline int __abs(int x) { return x < 0 ? -x : x; }

in GCC.  I have used this in <stdio.h> to make putc() readable (`#ifdef'ed
on __GNUC__, of course).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

meissner@tiktok.dg.com (Michael Meissner) (10/10/89)

In article <20042@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
| In general, the latter is true.  The proposed ANSI C standard requires
| that side effects take place exactly once, which effectively prohibits
| a straightforward macro version of abs() and labs().  One can still
| write, e.g.,
| 
| 	int abs(int);
| 	#define	abs(x)	__abs(x)
| 	static __inline int __abs(int x) { return x < 0 ? -x : x; }
| 
| in GCC.  I have used this in <stdio.h> to make putc() readable (`#ifdef'ed
| on __GNUC__, of course).

I know the above example is to demostrate the use of __inline, but
with GNU C, it does support a builtin abs function (though for some
reason it's not documented).  You can simplify the above to:

	#ifdef __GNUC__
	extern int abs(int);
	#define abs(x) __builtin_abs(x)
	#endif

Some machines have fast ways of doing abs (as compared to the branch
method above).  Gnu C also has the following builtin functions related
to abs:

	__builtin_labs(long)
	__builtin_fabs(double)
--
Michael Meissner, Data General.
Uucp:		...!mcnc!rti!xyzzy!meissner		If compiles were much
Internet:	meissner@dg-rtp.DG.COM			faster, when would we
Old Internet:	meissner%dg-rtp.DG.COM@relay.cs.net	have time for netnews?

keesan@bbn.com (Morris M. Keesan) (10/14/89)

In article <1392@cipc1.Dayton.NCR.COM> gmaranca@cipc1.Dayton.NCR.COM (Gabriel Maranca) writes:
>#define ABS(x) (((x) < 0) ? (-(x)) : (x))
. . .
>bill davidsen (davidsen@crdos1.crd.GE.COM) writes:
>>	1. instead of -(x) use (-(x)).
>Good point. I had overlooked this potential trap in my original macro.

I don't see why the extra parentheses are necessary, or their absence a trap.
#define ABS(x) ((x) < 0 ? -(x) : (x))
should work fine, because both < and - have higher precedence than ?:, and the
whole expansion is protected by the outermost parentheses.  It's not as if
-(x) were the full expansion of the macro.
------------------------------------------------------------------------------
Morris M. Keesan                    |
Internet: keesan@bbn.com            |
UUCP: Figure it out from the path   |