Is this a guaranteed signed/unsigned conversion?

C

CBFalconer

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

Michael Mair

CBFalconer said:
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) { Make that <=
if (j < 0) return -j;
else return j;
}
else if (LONG_MIN == j) return 1U + j + ULONG_MAX;
why not:
else if (j>0)
return j;
else if (j>= -LONG_MAX)
return -j;
else
return ((unsigned long)-(j-(LONG_MIN+LONG_MAX)))-(LONG_MIN+LONG_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.

If you assume that all bits are used and symmetrically, your
code will work.
Otherwise: what about LONG_MIN < -LONG_MAX-1?

See my annotations. (Burned my dinner over it, so I hope I
did not goof ;-))

Cheers
Michael
 
E

Eric Sosman

CBFalconer said:
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."
 
C

Christian Bau

CBFalconer said:
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.

I think it will work; I believe it is guaranteed that LONG_MIN +
LONG_MAX is either 0 or -1.

What about this:

unsigned long ulabs (long j) {
return j >= 0 ? (unsigned long) j : 0ul - j;
}

I think this should work. The case j >= 0 is obviously fine. If j < 0,
then evaluating (0ul - j) will convert j to unsigned long; since the
value of j cannot be represented ULONG_MAX + 1 will be added as often as
needed (that is once) giving (j + ULONG_MAX + 1). The subtract operation
gives - (j + ULONG_MAX + 1) and since the result is negative, ULONG_MAX
+ 1 is again added as often as necessary (that is once), giving -j.
 
L

Lawrence Kirby

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;

That's incorrect if ULONG_MAX is over twice as large as LONG_MAX.
Why not just return 1UL + LONG_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.

I've used similar to the following in the past:

unsigned long ulabs(long j)
{
return j>=0 ? (unsigned long)j : -(unsigned long)j;
}

Lawrence
 
S

Simon Biber

Lawrence said:
I've used similar to the following in the past:

unsigned long ulabs(long j)
{
return j>=0 ? (unsigned long)j : -(unsigned long)j;
}

I wonder if anyone ever made a C implementation that allows you to
choose at compile-time whether integers are stored as one's complement,
two's complement or signed magnitude? Of course, emulating operations on
a data type that is not native to the underlying hardware would be very
slow, but I imagine it would be useful for testing your code's
portability to esoteric computers without having them available on the
desktop.

The closest I know of is how GCC allows you to choose whether char is
signed or unsigned.
 
M

Malcolm

Simon Biber said:
I wonder if anyone ever made a C implementation that allows you to choose
at compile-time whether integers are stored as one's complement, two's
complement or signed magnitude?
That would be a really useful tool. Something that compiles C to byte code,
and then produces output based on all sorts of configurable and maybe
eccentric assumptions about type representation.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top