Main Site Documentation

2's compliment and inverting bits, HELP!


#1

Hi,
I am working with an LSM303DLHC sensor that has all sorts of 2’s compliment data. I am trying without any success to turn the data into actual values that make sense. I am trying to use the invert ‘~’ operator to invert a number if it is supposed to be negative. This is what happens:
Say the number I have (xhm) is 255 (MSB is 1, so it is negative). When I use the operation:

            if (xhm > 127)
            {
                xhm = ~xhm;
            }

I get -256 for the number, not -1. What am I doing wrong?
Thanks,
Tom


#2

Hi,
I got the operation to work:

            if (mag[0] > 127)
            {
                xh = (byte)((~mag[0] << 8) | ~mag[1]);
                xh *= -1;
                xh++;
            }
            else
            {
                xh = (mag[0] << 8) | mag[1];
            }

mag[0] and mag[1] are the two x magnetometer registers and xh is the final magnetometer reading. Is there a simpler way to do this? Why does the ~ invert not work?
Thanks,
Tom


#3

After combining two bytes into a short x I use this:

x = (x & 0x8000) != 0 ? -(short)~(x - 1) : x;

#4

Wow, that looks like Greek. Translation? Sorry, I am no great programmer, Mechanical Engineer by trade.
Thank you,
Tom


#5

Never mind, I think I get it. If the MSB of the short is a 1, then it is a 2’s compliment negative. Therefore run the

-(short)~(x - 1)

expression.
Thanks,
Tom


#6

You got it! :smiley:


#7

I am running into some interesting issues. The following code seems to convert the numbers by itself:

int m.x = (short)((mag[0] << 8) | mag[1]);

It generates positive and negative numbers. Is the code assuming somehow that this is a 2’s compliment number?

This is fine, but the next area I have to convert data is with a 12 bit number:

int a.x = (short)(((accel[0] << 8) | accel[1]) >> 4);

This is not automatically converted (possibly because bit 16 is never a 1?). I tried to use it with your code:


            short x = (short)(((accel[0] << 8) | accel[1]) >> 4);

            // Convert 2's compliment to integers
            int a.x = (x & 0x800) != 0 ? -(short)~(x - 1) : x;

I changed 0x8000 to 0x800 due to the 12 bits.

example - short x = 3087 - a negative number in a 12 bit 2’s compliment.
x & 0x800 = 0x800
a.x = 3087

The -(short)~(x-1) code does not seem to be running.
What am I missing?
Thanks,
Tom


#8

@ tommyvdp - What you need to do is called sign extension. Basically when you promote a signed number from a lower bit count to a higher bit count you need to extend the sign bit to fill the higher bit values. So in your case the you want to promote a 12 bit number to a 16 bit number so you need to extend the sign bit to fill the 4 additional bits.

Your 12 bit number 3087 is represented in binary as
110000001111

To put this into a short and have it be treated correctly you need extend the sign bit which would give the following bit pattern

1111110000001111
^^^^
These bits match the sign bit, if the sign bit had been a 0 ie. a positive number then these bits would have been all 0s.

So how do you sign extend the number, well here is some quick bit math (untested!!!)


short SignExtend(short value, signBit)
{
  int signMask = 1 << signBit;
  if ((value & singMask) != 0)
  {
    return (short)((short)(0xffff & ~(signMask - 1)) | value);
  }
  return value;
}

I hope that is right, but basically, we are checking if the sign bit is a 1 then we create an mask which will make all the high bits above the sign bit 1s and then we or the original value with the mask to get the sign extended value.

This code is a little naive, but I can’t think of a better way now. When I am at my dev PC I will see if my brain is working a little better. But hopefully this gets you going.


#9

@ taylorza - i have much to learn, thats for the post…


#10

Just would like to add for clarity that you do sign extension on 2’s complement form not on converted value.


#11

Good point, I should have been more explicit about that!

So here is my less naive solution, it leverages the fact that the compiler/CPU actually understands the concept of sign extension and will ensure that a signed value is correctly sign extended when you use the shift operators.


public static short SignExtend(short value, byte signBit)
{
  byte shift = (byte)(15 - signBit);
  return (short)((short)(value << shift) >> shift);
}

The first line determines the bit difference between the smaller and the larger type. Then the current value is shifted left by that difference, shifting the sign bit in the high bit position for the target type. To tell the compiler that we intend this to be a signed short, the value is explicitly cast to a short, now the compiler will ensure that any bit manipulations keep the sign intact. We can now shift right by 4 to restore the 12 bit value but keeping the sign extended through the bits that are now shifted away.