# Converting two's complement hex values to binary using PHP

I'm reading x-axis accelerometer data from an IC via the I2C bus using I2C-Tools' ( specifically `i2cget` ) in Linux. Here is the code that is reading these values:

``````\$OUT_X_L_A = shell_exec( 'i2cget -y 1 0x19 0x28' ) ... returns 0x20
\$OUT_X_H_A = shell_exec( 'i2cget -y 1 0x19 0x29' ) ... returns 0xfc
``````

The documentation for this accelerometer states that these values ( 0x20 and 0xfc ) are expressed in 2's compliment. This accelerometer has 16 bit resolution.

From another application I'm running that is reading this data in C, I believe that these values, when converted to a decimal value, should equal ~65,475 ( essentially no acceleration as the device is sitting on my desk ). Some other documentation I've found suggests that \$OUT_X_L_A is the Least Significant Register and \$OUT_X_H_A is the Most Significant Register.

I've done some research, but am generally unfamiliar with Two's Complement and LSB and MSB. How do I calculate this decimal value from the two hex values returned by the device using php?

Two's complement is a means of representing negative numbers in binary, using one bit to indicate positive or negative. We called these signed numbers since they have a sign, + or -. Unsigned numbers are always positive. So where a 16-bit "unsigned" number has the range 0 to 65535 (2^16), a 16-bit "signed" number in two's complement has the range -32768 to 32767 (+/- 2^15).

What you have is two 8-bit registers, making up the top and bottom half of a 16-bit value.

From another application I'm running that is reading this data in C, I believe that these values, when converted to a decimal value, should equal ~65,475

The C application is obviously then reporting the value as unsigned, since 65,475 is outside the range of a 16-bit signed value. For people more versed in dynamically typed languages like PHP than strongly typed ones like C, type conversion is harder to grasp, but note that if you convert 65,536 to a signed 16-bit two's complement number, you get -1. So, if that number from the application were properly expressed, it would actually be relatively close to zero (-992 to be exact).

The documentation for this accelerometer states that these values ( 0x20 and 0xfc ) are expressed in 2's compliment.

This is a misinterpretation. What's meant is that when you put the two halves together, you'll have a 16-bit number in two's complement. To do that, you need to shift the MSB up by 8 bits and add it to the LSB:

``````\$msb = 0xfc;
\$lsb = 0x20;
\$n = (\$msb << 8) + \$lsb;
``````

If you print this, you'll get 64544, because that what fc20 is in decimal. However, that's not the two's complement value -- that's the unsigned value. I am not really a php user (so forgive me if any of my syntax is clumsy), but from what I know of it, the way to convert would be via pack/unpack:

``````\$a = unpack(
's',
pack('v', \$n)
);
``````

If you print `\$a`, you now get the correct answer (-992). I'll leave it to you to read the php manual on pack() and unpack() but to explain briefly: `pack('v' \$n)` takes the value in \$n and returns a "binary string" version, presuming that \$n is an unsigned number. We have to do this because, while php uses signed integers, they are more than 16-bits long; using pack like this truncates it properly (so the binary string is 16 characters). `unpack('s')` takes that string, treats it as a 16-bit signed value, and puts it into a php integer (the only thing in the return array).