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