Author Topic: correct way to assemble 16bit twos complement value  (Read 1857 times)

0 Members and 1 Guest are viewing this topic.

Offline V_KingTopic starter

  • Regular Contributor
  • *
  • Posts: 113
  • Country: gb
correct way to assemble 16bit twos complement value
« on: December 08, 2015, 11:59:18 am »
Hello,

I am struggling to assemble a 16bit twos complement value from two 8 bit registers in C.

I've got:
Code: [Select]
int8_t data0 //LSB byte
int8_t data1 //MSB byte
int16_t data //16bit twos complement value

data = data1;
data = data << 8;
data = data | data0;

but kept getting just some number which makes no sense.
after some googling I found this approach:

Code: [Select]
int8_t data0 //LSB byte
int8_t data1 //MSB byte
int16_t data //16bit twos complement value

data = data1 & 0xFF;
data = data << 8;
data = data | (data0 & 0xFF);

is providing the numbers which look like correct readings. But I do not understand what difference does it '& 0xFF' make.

Would appreciate if someone could help me to fill in gaps with bitwise operation like this.

Thanks
 

Offline DJohn

  • Regular Contributor
  • *
  • Posts: 103
  • Country: gb
Re: correct way to assemble 16bit twos complement value
« Reply #1 on: December 08, 2015, 12:15:25 pm »
Code: [Select]
int8_t data0 //LSB byte
int8_t data1 //MSB byte
int16_t data //16bit twos complement value

data = data1 & 0xFF;
data = data << 8;
data = data | (data0 & 0xFF);

You should only need the second & 0xff here.

You've told it that data0 is a signed 8 bit number.  But then you or it with a signed 16 bit number (data), so the compiler has to convert it to 16 bits first.  If the top bit is set, it'll be interpreted as negative, and the entire top byte of the result will get set.  That's not what you want, so you're having to mask out that top byte.

If data0 and data1 were unsigned, your first code would work.  And that makes sense - you're not treating them as signed numbers themselves, but just collections of bits.
 

Offline Ian.M

  • Super Contributor
  • ***
  • Posts: 12860
Re: correct way to assemble 16bit twos complement value
« Reply #2 on: December 08, 2015, 12:20:43 pm »
Sign extension:  When C promotes a negative integer, it fills in the high byte(s) with all '1' bits to preserve the numeric value.  e.g. -11 is 0xFFF5 in 16 bits or 0xF5 in 8.

try:
Code: [Select]
data=data1*256+data0;
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: correct way to assemble 16bit twos complement value
« Reply #3 on: December 09, 2015, 10:19:57 am »
To be pedantic, the only compliant C is the one in Ian's answer.
The reason can be found in the standard (from C99 draft, 6.5.7, Bitwise shift operators, emphasis mine):
Quote
4
The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1×2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

What this means in layman terms is that if your shift affects the sign bit in a signed number, the result is not defined (actually, worse: undefined behavior).
Will it work? Yes, often, but it's not guaranteed to and it can change from compiler to compiler and even with revisions of the same compiler on the same architecture.

In general, shift operators are more safely used with unsigned integers.

Ian's answer  is only relying on the internal representation of integers to be 2's complement, that is an implementation defined aspect (not undefined: the compiler vendor must provide this information) and, nowadays, an almost safe assumption. It's also definitely cleaner and shorter.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline V_KingTopic starter

  • Regular Contributor
  • *
  • Posts: 113
  • Country: gb
Re: correct way to assemble 16bit twos complement value
« Reply #4 on: December 15, 2015, 12:00:41 pm »
thanks for all the answers. treating raw data as uint8_t worked perfectly. I always know that msb of the MSB is always a sign bit for signed integer, so as long as I shift it to correct place, the signed integer then should be interpreted correctly.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf