conversion from unsigned to signed with a value range shift

G

Guest

I want convert a value in a signed integer into an unsigned integer of the
same size, with a linear value shift that converts the lowest signed value
into the lowest unsigned value, and converts the highest signed value into
the highest unsigned value.

For 8 bit: For 16 bit: For 32 bit:

-128 -> 0 -32768 -> 0 -2147483648 -> 0
-127 -> 1 -32767 -> 1 -2147483647 -> 1
-1 -> 127 -1 -> 32767 -1 -> 2147483647
0 -> 128 0 -> 32768 0 -> 2147483648
1 -> 129 1 -> 32769 1 -> 2147483649
126 -> 254 32766 -> 65534 2147483646 -> 4294967294
127 -> 255 32767 -> 65535 2147483647 -> 4294967295

You can figure out the values for 64 bit, or other sizes. For particular
code, I'll know the size and thus the value difference.

I also want to do the exact reverse.

I can do this if a larger integer type is available, but first converting
the (un)signed value to a larger signed integer, doing arithmetic to shift
the value range, then convert back to a smaller size of reverse
signedness.

code #1:

char a;
int b;
unsigned char c;
a = -5; /* or otherwise assigned by some means */
b = (int) a;
b += 128;
c = (unsigned char) b;

Now variable c will have the value 123. The reverse would be:

code #2:

unsigned char a;
int b;
char c;
a = 123; /* or otherwise assigned by some means */
b = (int) a;
b -= 128;
c = (char) b;

Now variable c will have the value -5.

This is not an available option with the largest available integer type.
However, the first conversion can still be done by using the definition in
C99 6.3.1.3.2, which forces all signed values to be transposed to values
in an unsigned integer type by means of adding or subtracting one larger
than the largest value of the unsigned type, and C99 6.2.5.9 which avoids
overflow in unsigned integer arithmetic by wrapping module one larger than
the largest possible value.

So for 32 bit long integers (pretending there is nothing larger to use):

code #3:

long a;
unsigned long b;
a = -5; /* or otherwise assigned by some means */
b = (unsigned long) a;
b += 2147483648;

Now variable b will have the value 2147483643.

The catch is that the reverse if THIS does not appear to be compliant with
C99 even though on the gcc x86 implementation it works:

code #4:

unsigned long a;
long b;
a = 2147483643; /* or otherwise assigned by some means */
a -= 2147483648;
b = (long) a;

Now variable b does end up having the value -5. But I do not see anything
in the C99 standard that says it will have that value.

My goal is to find something that will do the conversion described in #4,
for any size of integer (without using a larger size), in a way that always
reverses the conversion described in #3, and does so in compliance with the
definition of C99.
 
P

Peter Nilsson

I want convert a value in a signed integer into an unsigned
integer of the same size, with a linear value shift that
converts the lowest signed value into the lowest unsigned
value, and converts the highest signed value into
the highest unsigned value.

For 8 bit:      For 16 bit:         For 32 bit:

-128 ->   0     -32768 ->     0     -2147483648 ->          0
-127 ->   1     -32767 ->     1     -2147483647 ->          1
  -1 -> 127         -1 -> 32767              -1 -> 2147483647
   0 -> 128          0 -> 32768               0 -> 2147483648
   1 -> 129          1 -> 32769               1 -> 2147483649
 126 -> 254      32766 -> 65534      2147483646 -> 4294967294
 127 -> 255      32767 -> 65535      2147483647 -> 4294967295

You're assuming two's complement with no trap representations,
and (for example) UINT_MAX == 2 * INT_MAX + 1.

They needn't hold true. If you can guarantee them, then something
like the following should work...

unsigned ciu(int x)
{
return 0u + x - INT_MIN;
}

int cui(unsigned x)
{
return x == 0u ? INT_MIN
: x < 0u + INT_MIN ? - (int) ( INT_MIN - x)
: (int) (x + INT_MIN);
}

If you can determine that double is big enough, then the
following may be more robust...

unsigned ciu(int x)
{
return (x - (INT_MIN + 0.)) * (1. + UINT_MAX)
/ (1. + INT_MAX - INT_MIN);
}

int cui(unsigned x)
{
return INT_MIN + x * (1. + INT_MAX - INT_MIN)
/ (1. + UINT_MAX);
}
 
G

Guest

| They needn't hold true. If you can guarantee them, then something
| like the following should work...
|
| unsigned ciu(int x)
| {
| return 0u + x - INT_MIN;
| }
|
| int cui(unsigned x)
| {
| return x == 0u ? INT_MIN
| : x < 0u + INT_MIN ? - (int) ( INT_MIN - x)
| : (int) (x + INT_MIN);
| }

I figured I might have to do something like that. But I womder if the
optimization on platforms where the implementation defined simple solution
would work, will reduce this to just that same machine code, anyway (by
detecting that the various paths are all just ultimately the same thing).

On machines like x86 and others I've worked with, it comes down to just
flipping the high order bit state for conversion either way. Doing it in
C code that complies with the standard is one thing (and is desired).
Doing it in a way that can result in that simple machine code assuming a
decent optimizer is also desired (or else serves as an example of code
that humans can optimize better).
 
P

Peter Nilsson

| They needn't hold true. If you can guarantee them, then
| something like the following should work...
|
|  unsigned ciu(int x)
|  {
|    return 0u + x - INT_MIN;
|  }
|
|  int cui(unsigned x)
|  {
|    return x == 0u           ?              INT_MIN
|         : x <  0u + INT_MIN ? - (int) (    INT_MIN - x)
|         :                       (int) (x + INT_MIN);
|  }

I figured I might have to do something like that.

Simple question: why do you have to do it at all?
 
F

Flash Gordon

Personally I would prefer a cast to unsigned int, I think it expresses
the intent more clearly.

I would simply use 0, not 0u.

Why add 0u? It will be converted anyway since it is being compared with
an unsigned int. If you want to make it explicit then a cast would make
more sense.

You don't need this cast, although I suppose for consistency it is not
too bad.
Simple question: why do you have to do it at all?

I've come across interfaces with this kind of encoding before. It is
sometimes a requirement.
 
G

Guest

| (e-mail address removed) wrote:
|> | They needn't hold true. If you can guarantee them, then
|> | something like the following should work...
|> |
|> |  unsigned ciu(int x)
|> |  {
|> |    return 0u + x - INT_MIN;
|> |  }
|> |
|> |  int cui(unsigned x)
|> |  {
|> |    return x == 0u           ?              INT_MIN
|> |         : x <  0u + INT_MIN ? - (int) (    INT_MIN - x)
|> |         :                       (int) (x + INT_MIN);
|> |  }
|>
|> I figured I might have to do something like that.
|
| Simple question: why do you have to do it at all?

Certain calculations work better when the range of values is shifted into
all positive. C99's / and % operators are examples. Truncation towards
zero is not correct in all cases. Sometimes, truncation downwards is what
is needed. A simple way to achieve that is transposing all values into a
positive range and adjusting for the offset from the divisor.
 

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

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,527
Members
44,999
Latest member
MakersCBDGummiesReview

Latest Threads

Top