CBFalconer wrote:
Consider:
#include <stdlib.h>
/* An elementary optimizer is expected to remove most code */
/* Return the absolute value */
unsigned long ulabs(long j)
{
if (0 == LONG_MIN + LONG_MAX) {
if (j < 0) return -j;
else return j;
}
else if (LONG_MIN == j) return 1U + j + ULONG_MAX;
else if (j < 0) return -j;
else return j;
} /* ulabs */
Is this guaranteed to convert all long values to their absolute
unsigned long equivalent, assuming that the desired result is
representable as an unsigned long. Does it work over 2's, 1's
complement and sign-magnitude? Is there a better method? labs()
is not guaranteed.
Let's see: The first part handles ones' complement and
signed magnitude, where negation of LONG_MIN is possible.
Straightforward, and correct as far as I can see. By the
way, the `0 == LONG_MIN + LONG_MAX' test could be made
with `#if' instead of with `if', reducing the reliance on
the optimizer's ability to eliminate dead code.
In the two's complement part, the conversion of LONG_MIN
seems wordier than need be: as far as I can tell, it gives
the same result as `return j'. Either way, the result should
be correct if ULONG_MAX == LONG_MAX * 2UL + 1UL, which is the
case on every implementation I've run into. However, I think
the Standard permits perversity, and two kinds of perversity
could cause trouble:
ULONG_MAX == LONG_MAX (that is, the sign bit of
`long' corresponds to a padding bit in `unsigned
long'). In this case your quest is hopeless --
but your assumption excludes it, so perhaps we
needn't worry. Another #if could trigger an
appropriate #error directive if desired.
ULONG_MAX == LONG_MAX * 4UL + 3UL (for example;
this happens if `long' has padding bits that
correspond to value bits of `unsigned long').
In this case, ulabs() would return much too
large a value.
I think there's a simple repair, though. For a negative
two's complement value, first add unity, then negate (this is
known to be safe), then convert to `unsigned long', and finally
add another unity to cancel the first (now negated) addition:
if (j < 0) return -(j + 1) + 1UL;
else return j;
(Note that the first `return' expression is *not* equivalent
to `1UL - (j + 1)', because the conversion to `unsigned long'
occurs at the wrong point in the proceedings. I'd recommend
a goodly block of comments here to discourage tidy-minded
people from making the "obvious" rearrangement -- either that,
or write it as `-++j+1UL' to scare them all away. ;-)
As far as I can see, this will cover all cases except
ULONG_MAX == LONG_MAX, already excluded by assumption. It
would even handle ones' complement and signed magnitude,
eliminating the special case at the cost of a smidgen of
unnecessary code.
Of course, I may have overlooked something. "Trust,
but verify."
--
Er*********@sun.com