Joining 2 char to 1 short

S

Steffen Loringer

Hi,

I'm using the following function to join 2 char (byte) into one short on
a 32 bit X86 platform:

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
unsigned short val = 0;
val = a;
val <<= 8;
val |= b;
return val;
}

Will this also work if compiled on a PowerPC? Are there better ways to
do it?

Thanks a lot!

Steve
 
R

Richard Bos

Steffen Loringer said:
I'm using the following function to join 2 char (byte) into one short on
a 32 bit X86 platform:

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
unsigned short val = 0;
val = a;
val <<= 8;
val |= b;
return val;
}

Will this also work if compiled on a PowerPC?

Depends on what you want. Of course, it _does_ assume that CHAR_BIT is 8
and sizeof (short) is at least 2. Both of those are very common; neither
is guaranteed. It is possible to encounter devices where CHAR_BIT is 32,
and sizeof (char) == sizeof (short) == sizeof (int) == 1.

However, since an unsigned short must be able to hold at least 2**16-1,
and therefore be at least 16 bits wide, CHAR_BIT being exactly 8 already
implies sizeof (short) being at least 2. (The implication doesn't hold
other way; for example, a system where sizeof (short) is 2, but CHAR_BIT
is 9 is quite legal. 36-bit word, char is a quarter word, short half of
one.)

OTOH, the assumption that CHAR_BIT is 8 can be removed by the
marvelously exotic expedient of replacing 8 by CHAR_BIT. The assumption
that sizeof (short) >= 2 is harder to get rid of.
Are there better ways to do it?

Yes; provided you are willing to put a note in the documentation that
the code assumes that sizeof (short) >= 2, your entire function can be
replaced by

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
return (a<<CHAR_BIT) + b;
}

Richard
 
S

Steffen Loringer

Yes; provided you are willing to put a note in the documentation that
the code assumes that sizeof (short) >= 2, your entire function can be
replaced by

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
return (a<<CHAR_BIT) + b;
}

Richard

So I assume bit-shifting in this function is independend concerning
big/low endian systems!? Does the compiler take care for correct
shifting in both cases?
 
C

Chris Dollin

Steffen said:
So I assume bit-shifting in this function is independend concerning
big/low endian systems!? Does the compiler take care for correct
shifting in both cases?

There isn't a bigendian/littleendian issue to worry about here. Why did
you think there was?
 
P

pete

Steffen said:
So I assume bit-shifting in this function is independend concerning
big/low endian systems!? Does the compiler take care for correct
shifting in both cases?

Yes.
The meaning of the code might be more obvious
if done with arithmetic operations.

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
return (UCHAR_MAX + 1U) * a + b;
}

That also avoids the undefined behavior associated
with shifting too far, if sizeof(short) is greater than one.
And, (UCHAR_MAX + 1U) * a, is likely to be compiled t
o code which is just as fast as the shifting code.

If sizeof(short) is equal to one, then the function returns b,
so the "sizeof (short) > 1" documentation
that Richard Boss mentioned, still applies.
 
S

Sven Fülster

Steffen Loringer said:
So I assume bit-shifting in this function is independend concerning
big/low endian systems!? Does the compiler take care for correct
shifting in both cases?

<OT>
Have a look at the functions
ntohs() [htons()] which convert a short value from network byte order (big
endian) to host byte order (whatever your host system is) [and vice versa]
</OT>
 
?

=?ISO-8859-1?Q?=22Nils_O=2E_Sel=E5sdal=22?=

Sven said:
Steffen Loringer said:
So I assume bit-shifting in this function is independend concerning
big/low endian systems!? Does the compiler take care for correct
shifting in both cases?

<OT>
Have a look at the functions
ntohs() [htons()] which convert a short value from network byte order (big
endian) to host byte order (whatever your host system is) [and vice versa]
</OT>

Or get/put them directly in the right order(big endian in this case) :) ?
uint16_t unpack16(uint8_t buf[2])
{
return buf[0]<<8 | buf[1];
}
void pack16(uint16_t val,uint8_t buf[2])
{
buf[0] = val>>8;
buf[1] = val&0xff;
}
 
P

pete

pete said:
Yes.
The meaning of the code might be more obvious
if done with arithmetic operations.

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
return (UCHAR_MAX + 1U) * a + b;
}

That also avoids the undefined behavior associated
with shifting too far, if sizeof(short)
is

should be "isn't"
 
F

Frederick Gotham

Steffen Loringer posted:
Hi,

I'm using the following function to join 2 char (byte) into one short on
a 32 bit X86 platform:

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
unsigned short val = 0;
val = a;


Ridculously inefficient.

Why set a variable's value to zero, and the immediately give it another
value?

val <<= 8;


val <<= CHAR_BIT; /* A byte isn't always 8 bits */

val |= b;
return val;
}

Will this also work if compiled on a PowerPC? Are there better ways to
do it?


Not necessarily. If unsigned char has 16 value bits and so does short,
then you won't get the result you want.
 
F

Frederick Gotham

pete posted:
Yes.
The meaning of the code might be more obvious
if done with arithmetic operations.

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
return (UCHAR_MAX + 1U) * a + b;
}


If I'm not mistaken, (UCHAR_MAX + 1U) is well within its rights to
evaluate to zero. Imagine the following system:

unsigned char: 32 bits, no padding
unsigned short: 32 bits, no padding
unsigned int: 32 bits, no padding
unsigned long: 32 bits, no padding
 
T

Tom St Denis

Frederick said:
Ridculously inefficient.

Why set a variable's value to zero, and the immediately give it another
value?

It's poorly written not inefficient. I suggest you look at the output
of an optimizing compiler from time to time :)
val <<= CHAR_BIT; /* A byte isn't always 8 bits */

Um, what if you want to pack 8 bit words into a larger word? For [say]
network coding you want to be explicit.
Not necessarily. If unsigned char has 16 value bits and so does short,
then you won't get the result you want.

Which is why he shifted by 8 and not CHAR_BIT. Chances are the inputs
are ranged limited to 0..255.

Speaking as someone who writes code on an x86 and has it excuted on all
manners of MIPS, PPC, SPARC, IA64 and others I think I know what I'm
talking about here.

If the intent was to pack two 8 bit values into a single integer the
routine

unsigned pack(unsigned char a, unsigned char b) { return (a<<8)|b; }

Will work fine.

Of course in my code I use macros for all this and I explicitly cast
everything to char or long [unsigned of course] just to cover my bases.

Tom
 
P

pete

Frederick said:
pete posted:


If I'm not mistaken, (UCHAR_MAX + 1U) is well within its rights to
evaluate to zero.

Yes.

The part of my post which you snipped, addresses that issue:

"If sizeof(short) is equal to one,
then the function returns b,
so the "sizeof (short) > 1" documentation
that Richard Boss mentioned, still applies."
 
F

Frederick Gotham

Tom St Denis posted:


Speaking as someone who writes code on an x86 and has it excuted on all
manners of MIPS, PPC, SPARC, IA64 and others I think I know what I'm
talking about here.


That shouldn't be an issue if you're writing portable code.

By the way, was the injection of arrogance intentional?


unsigned pack(unsigned char a, unsigned char b) { return (a<<8)|b; }

Will work fine.


I'd probably do something like:

#include <assert.h>

unsigned short Pack( unsigned char a, unsigned char b )
{
assert( a <= 255 );
assert( b <= 255 );

return ( a << 8 ) | b;
}
 
K

Keith Thompson

Steffen Loringer said:
Hi,

I'm using the following function to join 2 char (byte) into one short on
a 32 bit X86 platform:

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
unsigned short val = 0;
val = a;
val <<= 8;
val |= b;
return val;
}

Will this also work if compiled on a PowerPC? Are there better ways to
do it?

It will do what it says. Whether it will do what you want depends on
exactly what you want, which you haven't quite told us.

There are many possible ways to join two chars into a short. What
exactly are you trying to accomplish? Do you want the value of "a" in
the high-order bits of the result? Do you want it in the leftmost
(lowest address) portion of the result?
 
R

Richard Bos

Yes. The shift operators work on values, not on representations. This
code always puts a in the higher-value byte of the return value, and b
in the lower-value one.
<OT>
Have a look at the functions ntohs() [htons()]

Or rather, don't. Not only will they make your code less portable, they
will not add any functionality in this case.

Richard
 
D

Dave Thompson

To be picky, at least ( (unsigned short)a << CHAR_BIT ) + b
or (as I prefer) to either use both bitwise
(unsigned short)a << CHAR_BIT | b /* precedence ok */
or both arithmetic as you do below.

Otherwise unsigned char of 8 or even 9 or 12 bits can promote to
signed int of as little as 15+1 bits, in which left shift of some
uchar values by CHAR_BIT overflows and produces U.B.

The meaning of the code might be more obvious
if done with arithmetic operations.

unsigned short joinUnsigShort(unsigned char a,unsigned char b)
{
return (UCHAR_MAX + 1U) * a + b;
}

That also avoids the undefined behavior associated
with shifting too far, if sizeof(short) is greater than one.

Corrected to isn't. But size or even width of short doesn't matter. It
is U.B. if unsigned int is the same width as unsigned char (so the
promoted value is being shifted by >= its width) OR if (signed) int is
wider than char but not 'more than twice' (precisely, does not have at
least twice as many value/magnitude bits, plus sign).
And, (UCHAR_MAX + 1U) * a, is likely to be compiled t
o code which is just as fast as the shifting code.

If sizeof(short) is equal to one, then the function returns b,
so the "sizeof (short) > 1" documentation
that Richard Boss mentioned, still applies.


- David.Thompson1 at worldnet.att.net
 

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,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top