Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C - three bytes into one signed int

I have a sensor which gives its output in three bytes. I read it like this:

unsigned char byte0,byte1,byte2;

byte0=readRegister(0x25);
byte1=readRegister(0x26);
byte2=readRegister(0x27);

Now I want these three bytes merged into one number:

int value;
value=byte0 + (byte1 << 8) + (byte2 << 16);

it gives me values from 0 to 16,777,215 but I'm expecting values from -8,388,608 to 8,388,607. I though that int was already signed by its implementation. Even if I try define it like signed int value; it still gives me only positive numbers. So I guess my question is how to convert int to its two's complement?

Thanks!

like image 933
muliku Avatar asked Jan 03 '23 13:01

muliku


2 Answers

What you need to perform is called sign extension. You have 24 significant bits but want 32 significant bits (note that you assume int to be 32-bit wide, which is not always true; you'd better use type int32_t defined in stdint.h). Missing 8 top bits should be either all zeroes for positive values or all ones for negative. It is defined by the most significant bit of the 24 bit value.

int32_t value;
uint8_t extension = byte2 & 0x80 ? 0xff:00; /* checks bit 7 */
value = (int32_t)byte0 | ((int32_t)byte1 << 8) | ((int32_t)byte2 << 16) | ((int32_t)extension << 24);

EDIT: Note that you cannot shift an 8 bit value by 8 or more bits, it is undefined behavior. You'll have to cast it to a wider type first.

like image 134
Grigory Rechistov Avatar answered Jan 05 '23 02:01

Grigory Rechistov


#include <stdint.h>
uint8_t byte0,byte1,byte2;
int32_t answer;

// assuming reg 0x25 is the signed MSB of the number 
// but you need to read unsigned for some reason
byte0=readRegister(0x25);
byte1=readRegister(0x26);
byte2=readRegister(0x27);

// so the trick is you need to get the byte to sign extend to 32 bits
// so force it signed then cast it up
answer = (int32_t)((int8_t)byte0); // this should sign extend the number
answer <<= 8;
answer |= (int32_t)byte1; // this should just make 8 bit field, not extended
answer <<= 8;
answer |= (int32_t)byte2;

This should also work

answer = (((int32_t)((int8_t)byte0))<<16) + (((int32_t)byte1)<< 8) + byte2;

I may be overly aggressive with parentheses but I never trust myself with shift operators :)

like image 23
bd2357 Avatar answered Jan 05 '23 03:01

bd2357