rescale signed to unsigned (short) int

A

atrac

Hello.
In an embedded system, I am reading a binary file. In this application
data are stored in the file as 16bit signed, ranging from -32768 to
32767
Now, I need to rescale this to an unsigned 16 bit, ranging from 0 to
65535.
I do it in this way:

unsigned short int udata; //16 bit unsigned
short int sdata; //16 bit signed
// some code here loads 16 bit signed value into "sdata", then:
if(sdata & 0x80) {
//this is a negative number. Complement, convert to unsigned
and rescale
udata = 32767 - (unsigned short int) ~sdata;
} else {
//this is a positive number. Convert to unsigned and rescale
udata = 32768 + (unsigned short int) sdata;
}

Now, to optimize the code, I thought I could also use:
udata = ((unsigned short int) (sdata ^ 0x8000) ) ;
however, this does not work when sdata = -1 (binary all 1's).
Is there a way to smartly solve this issue?
I mean to optimize the code to use less instructions.
Am I missing anything?
Of course this is the 16 bit case but the question could be extended
to 32 bit int.
 
N

Niklas Holsti

atrac said:
Hello.
In an embedded system, I am reading a binary file. In this application
data are stored in the file as 16bit signed, ranging from -32768 to
32767
Now, I need to rescale this to an unsigned 16 bit, ranging from 0 to
65535.
I do it in this way:

unsigned short int udata; //16 bit unsigned
short int sdata; //16 bit signed
// some code here loads 16 bit signed value into "sdata", then:
if(sdata & 0x80) {
^^^^
Typo? Should be 0x8000 to get the sign bit (bit 15).
//this is a negative number. Complement, convert to unsigned
and rescale
udata = 32767 - (unsigned short int) ~sdata;
} else {
//this is a positive number. Convert to unsigned and rescale
udata = 32768 + (unsigned short int) sdata;
}

Now, to optimize the code, I thought I could also use:
...

I take it that you don't actually want to "scale" (multiply), but only
to "shift" the zero-point of the numbers so that -32768 is transformed
to (unsigned) 0, -32767 to 1, and so on, until +32767 is transformed to
65535. If so, you just need to add 32768, as in

int16_t sdata;
uint16_t udata;
...
udata = ((uint16_t)sdata) + 32768;

HTH,
 
J

Jens Thoms Toerring

atrac said:
In an embedded system, I am reading a binary file. In this application
data are stored in the file as 16bit signed, ranging from -32768 to
32767
Now, I need to rescale this to an unsigned 16 bit, ranging from 0 to
65535.
I do it in this way:
unsigned short int udata; //16 bit unsigned
short int sdata; //16 bit signed
// some code here loads 16 bit signed value into "sdata", then:
if(sdata & 0x80) {
//this is a negative number. Complement, convert to unsigned
and rescale
udata = 32767 - (unsigned short int) ~sdata;
} else {
//this is a positive number. Convert to unsigned and rescale
udata = 32768 + (unsigned short int) sdata;
}
Now, to optimize the code, I thought I could also use:
udata = ((unsigned short int) (sdata ^ 0x8000) ) ;
however, this does not work when sdata = -1 (binary all 1's).
Is there a way to smartly solve this issue?
I mean to optimize the code to use less instructions.

Careful here, the number of instructions can hardly be interfered
from the C code, there is no simple mappimg between e.g. the number
of operators in a line of C code and the number of operations you
will end up with when the compiler is done with that line... Your
best bet is probably to make the code as simple as possible to
give the compiler a good chance to convert it into the optimal
machine code.
Am I missing anything?
Of course this is the 16 bit case but the question could be extended
to 32 bit int.

As far as I understand what you're trying to do a simple

udata = 0x8000U + sdata;

should do the job, no differentiation between negative and
positive numbers needed. And for a more general solution
(at least if all available bits there are are used as they
normally are for the traditional integer types, but bewate
of types like int17_t where is unlikely to be the case) some-
thing like

udata = ( 1U < ( CHAR_BIT * sizeof udata - 1 ) ) + sdata;

should work (note: CHAR_BIT is defined in <limits.h>).

Regards, Jens
 
E

Ersek, Laszlo

I take it that you don't actually want to "scale" (multiply), but only
to "shift" the zero-point of the numbers so that -32768 is transformed
to (unsigned) 0, -32767 to 1, and so on, until +32767 is transformed to
65535. If so, you just need to add 32768, as in

int16_t sdata;
uint16_t udata;
...
udata = ((uint16_t)sdata) + 32768;

(It's nice to see that my idea wasn't broken completely -- I'll say
"thanks" for it in the best c.l.c way: by nit-picking :))

The above can invoke undefined behavior on a brain-damaged platform where
"unsigned int" has 17 value bits, and "signed int" has 16 value bits and a
sign bit.

Let sdata be -1 --> ((uint16_t)65535) will be promoted to an "int", the
other operand is also an int, but the result can't be represented as an
int.

Yes, yes, the OP did tell us about 32 bit ints, just as I relied on
"short"s being specified to have 16 bits. Sorry, couldn't resist :)

Thanks,
lacos
 
S

Seebs

I mean to optimize the code to use less instructions.

This is almost certainly a very, very, bad idea.

Quick summary:

If you don't *already* know the answer, because you know exactly what
the generated assembly will be, the chances are about 99% that you should
NOT be trying to "optimize" the code to use fewer instructions -- you
should be writing what you mean as clearly as possible and letting
the compiler do it.

I'm also not sure what you mean by "rescale". As someone else pointed
out, it seems a lot like you just mean "x = x + 32768".

-s
 
A

atrac

I'd recommend the "unified" expression

        (unsigned short)((unsigned short)sdata + 32768u)
....

Thank you for the tip, it makes sense.
I understand what you say, however I was wondering whether the
compiler always promote to int or if an overflow exception could
occur.
On monday I will have a look to the assembler generated by both your
code and my code.
 
L

lawrence.jones

atrac said:
I understand what you say, however I was wondering whether the
compiler always promote to int or if an overflow exception could
occur.

The compiler always promotes to int (or unsigned int, in the rare case
where short and int have the same range). The expression is guaranteed
portable.
 
J

Jens Thoms Toerring

atrac said:
Thank you for the tip, it makes sense.
I understand what you say, however I was wondering whether the
compiler always promote to int or if an overflow exception could
occur.

With this method you actually rely on the overflow when dealing
with negative values in 'sdata' (and, by the way, there aren't
any exceptions in C, that's C++). The whole thing works just be-
cause when you cast the negative short int it will result in an
(unsigned) value larger than SHRT_MAX. And when you than add it
to another unsigned short larger than SHRT_MAX the results is a
value larger than USHRT_MAX. The C standard now guarantees that
this will "wrap around" when assigned (or cast) to an unsigned
short. The same trick, of course, also works with int's and
long's (always assuming that the maximum of the unsigned type
equals the maximum of the signed type minus its minimum).

Regards, Jens
 
L

lawrence.jones

Jens Thoms Toerring said:
With this method you actually rely on the overflow when dealing
with negative values in 'sdata'

No, you rely on the "wrap around" (or "modulus") behavior that's
required when converting a signed value to an unsigned type and when
performing arithmetic on unsigned types. That's not overflow.
 
E

Ersek, Laszlo

Or just do...

udata = sdata + 32768u;

You are right. "sdata" will be promoted to an "int" first (with no change
to the value), then converted to "unsigned int" for addition. This
conversion entails a "reduction to non-negative" modulo (UINT_MAX+1). As
part of the addition, the mathematical result will also be reduced modulo
(UINT_MAX+1).

Since (UINT_MAX+1) is an integral multiple of (USHRT_MAX+1) -- see
6.2.6.2p1 for that --, both reductions modulo (UINT_MAX+1) won't change
the remainder modulo (USHRT_MAX+1).

x === n (mod m1)

m2 := t * m1

x + u * m2
= x + (u * t) * m1
=== x (mod m1)
=== n (mod m1)

Nicely spotted, thank you.

lacos
 

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,769
Messages
2,569,582
Members
45,060
Latest member
BuyKetozenseACV

Latest Threads

Top