Two's Complement

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am working with a sensor that outputs a 24bit value

 

I acquire the bytes without any problem

 

My first question

 

I tried to concatenate the three bytes into a 32bit int

 

uint32_t    data     = (byte_array[3]<<16)|(byte_array[1]<<8)|(byte_array[0]);

And I get the error

 

avr left shift count >= width of type

I am sure this is me being dumb but I can't see what the issue is with shifting 16 bits?

 

Now time for the real question

 

Of the 24 bits the first 16 are the integer part and the last 8 are the fractional part

 

The integer part is 2's complement to allow for negative values and I am so noob its actually the first time I have had to deal with this

 

if I concatenate the 16bit integer part like this

 

int16_t  temp_int   = ((byte_array[2]<<8)|(byte_array[1]));

I have a signed 16bit integer and this result will be all I need is this right? I was reading something that said if I want to convert a twos complement number I have to negate all bits and then add 1 but I think thats if I am working on raw bits

 

So my question in a nutshell is

 

If I use a signed int then does this convert a twos complement number for me without much thought?

 

Edit

 

I think my first problem was trying to shift an 8bit value 16bits and its obviously not long enough

 

uint32_t    data     = ((uint32_t)byte_array[3]<<16)|(byte_array[1]<<8)|(byte_array[0]);

Seems to fix it but I think I should cast the 2nd byte to a 16bit

Last Edited: Thu. Jul 9, 2015 - 11:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For the shift thing typecast the element to 32bits (uint32_t) or (int32_t) so it can be shifted 16 places (otherwise it won't fit).

 

For the sign extension you basically need to check bit 24 and if it is set (the non fraction is negative) then set the top byte to 0xFF to sign extend into 32 bits.

 

Something like:

if (data & 0x800000) {
    data |= 0xFF000000;
}

Or you could operate on the data as it was before the shift

if (byte_array[3] & 0x80) {
    data |= 0xFF000000;
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Cliff

 

I understand the first question now, I was being dumb

 

I am confused about my second

 

I understand that the MSB represents sign, if its a 1 then we have a negative number I totally understand this

 

So I can check the first bit and even do

 

-1^(MSB)

 

which could work to force the values negative I would then manipulate the bits to work out the value

 

Wouldn't

 

int16_t  temp_int   = ((byte_array[5]<<8)|(byte_array[4]));

Work it all out for me

 

In my mind I have told it that its a signed int so the uC does the work

 

I might be way off

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

((int32_t)(int8_t)(high_byte))<<16

will get the sign right.

If high_byte is not already a signed type, the conversion to int8_t is implementation-defined and avr-gcc does the right thing.

The conversion to int32_t is value-preserving.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess we don't actually know what type your byte_array[] is. Also if the plan is to end up with a signed result why is data of type uint32_t?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks guys

 

The byte array is initialised

 

 uint8_t    byte_array[6]    = {0,0,0,0,0,0};

 

My idea is that the sensor sends data and I just need to store the results to be manipulated later, maybe I should use signed but it confuses me because the other bytes aren't two's complement

 

I receive the bytes without issue

 

why is data of type uint32_t

I was just trying to get it into a 24bit value because thats what the datasheet did then worked all kinds of magic with bitshifts etc

 

I think that this should work but I am unable to try it on hardware at this time hence the thread

 

int16_t  temp_int   = ((byte_array[5]<<8)|(byte_array[4]));

byte_array stores the data and it doesn't know that its in twos complement its just data, its then my job to manipulate it

 

Is there anything wrong with my thinking here?

 

Much appreciated

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh I see you are just ditching the fractional part and want to put the other 2 or three bytes into an int16_t, rather than create a 24 bit value held in an int32_t, is that right? In that case, yes, if you do something like Michael suggested then it should work. (but replaces his (int32_t) with (int16_t)).

 

EDIT: forgot to say that it is about 1 million times easier to mess about with this kind if stuff on a PC using a PC compiler and debugging .exe (or executables on Linux). As long as you use <stdint.h> types the same code should just port and work on the AVR too.

 

EDIT: like this...

$ cat test3.c
#include <stdio.h>
#include <stdint.h>

uint8_t byte1 = 0xFC, byte2 = 0x3C;

int main(void) {
	int16_t result;

	result = ((int8_t)byte1)<<8 | byte2;
	printf("%hd\n", result);
	return 0;
}
$ gcc test3.c -o test3
$ ./test3
-964

 

Last Edited: Thu. Jul 9, 2015 - 02:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quite honestly,  if you bought a 24-bit sensor,   you might just as well use all the bits.

As skeeve has shown,  the Compiler will look after the sign if you use some appropriate casts.   However,   you can go seriously wrong if you try to use signed variables in the wrong place.

 

Just treat SPDR as an uint8_t and shift each chunk into the correct place of an uint32_t.   If bit23 is set,   set bit24-31.   (i.e. sign extension)

Now you have a valid signed 32 bit number.    I bet that you could display directly with "%ld" and get human readable results.

 

If you said which sensor you are using,   we could possibly give you more accurate advice.

 

David.

Last Edited: Thu. Jul 9, 2015 - 03:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Cliff, its much appreciated

 

When we do the Melbourne Freaks in England I will buy you a drink

 

The good news is I got it working, I tested it and it swings negative without issue so many thanks

 

Quite honestly,  if you bought a 24-bit sensor,   you might just as well use all the bits.

Yeah David I have to, its a 24bit sensor but some bits are integer part and some are fractional, it requires bit masking and things because its actually 15bit for the fraction and 9 bits for the integer so some jiggery pokery is mandatory

 

If you said which sensor you are using,   we could possibly give you more accurate advice

This sensor is a prototype from a largeish sensor manufacturer company and hence it isn't even realeased yet so there is literally no documentation except the A4 sheet the development engineer passed to me, there is no part number yet

 

I am lucky in that I work for an enormous corporation and we have eye watering expensive machines and equipment so I can test sensors with conditions that most companies cant replicate, Its a two way street, we get access to sensors before they are out and the company gets free test data where we put the sensors through their paces 

 

I find it great fun if I am honest

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The following is required to work under extremely few restrictions:

if(((uint32_t)only24bits) & (1UL<<23)) only24bits-=1L<<24;

only24bits must have a signed type and must have a value in the range 0..2**24-1 .

In twos complement, the latter requires zeros in bits 24 and up.

No implementation-defined behaviour is required.

 

only24bits=((int32_t)(((uint32_t)only24bits)<<8))/0x100;

assumes that only24bits is a signed type and that avr-gcc does the right thing when casting from unsigned to signed.

Garbage in bits 24 and up are allowed.

Iluvatar is the better part of Valar.