0

I have a pointer of (signed) int8_t

int8_t *data

It comes from a netcdf file, in which data is encoded as a int8 array. To limit the file size and use the cheapest representation as possible, it is in reality a successions of signed integers of different sizes (4,8,16 and 32 bits), whose organization is specified elsewhere and not relevant to that question.

If I want to interpret the data as int16 or int32 I can just do (updated after StoryTeller comment on undefined behaviour of the use of a reinterpret_cast in this case):

int16_t data_16
memcpy(&data_16, &data[index], sizeof(int16_t));
int data_16_32 = data_16

However, if the data is to be interpreted as int4 (two int4 in a single int8 memory space), how can I retrieve the int4 values? The int4 type does not exist in C.

My question in short: Ho to interpret a int8 variable as two int4

I guess this topic could be useful, but i do not really understand: https://codereview.stackexchange.com/questions/30593/split-a-long-integer-into-eight-4-bit-values

seb007
  • 488
  • 3
  • 19

2 Answers2

3

Mask off the two 4bit integers, if they are signed you need to be careful, unsigned not so much.

For unsigned integers you can simply do:

uint8_t original = 0xab;  // Source 8 bit data
uint8_t low = original & 0x0f // Mask off the high bits and leave just the low bits
uint8_t high = original >>4; // Move the high bits to the low bits

If you have 4 bit signed integers you will need to shift them all the way left BEFORE moving them right again to maintain the sign status

int8_t original = 0xef; // Source 8 bit data -2 (high)and -1 (low)
int8_t low = original << 4;  // Move left 4 bits first
low = low >> 4;  // Move bits right but maintain sign
int8_t high = original >> 4; // Move right 4 bits

Hopefully this helps

Graeme
  • 1,643
  • 15
  • 27
  • Nice answer. I'd suggest two changes: use `int8_t` for signed and use `uint8_t` for unsigned. `int8 high = original >>4;` would fail for unsigned if `original` is signed. – Christoph Diegelmann Jul 03 '17 at 13:13
  • 1
    Approved your edit @Christoph thanks. I guess the use of uint8_t and int8_t would affect >> being logical or arithmetic too (had read it is was usually arithmetic but implementation dependant). – Graeme Jul 03 '17 at 14:13
  • When I try your code (unsigned version) and print the results: `int8_t original = 0xef; int8_t low = (original << 4) >> 4; int8_t high = original >> 4; printf("original %" PRIi8 " low %" PRIi8 " high %" PRIi8 "\n", original, low, high);`, I get wrong results for the high integer: `original -17 low -17 high -2` – seb007 Jul 04 '17 at 08:02
  • 1
    It looks like a compiler optimization or something (Intel compiler, windows). If I split the command in two lines it works: `int8_t original = 0xef; int8_t low = original << 4; low = low>> 4; int8_t high = original >> 4; printf("original %" PRIi8 " low %" PRIi8 " high %" PRIi8 "\n", original, low, high); exit(-1);` As a result I get: `original -17 low -1 high -2`. – seb007 Jul 04 '17 at 08:12
  • Thanks @seb007 I have amended my answer to take that into account. – Graeme Jul 04 '17 at 10:12
2

One way to do it would be to use shifts and masks, like so:

unsigned high4bits = int8 >> 4; // shift high 4 bits into lower 4
unsigned low4bits = int8 & 0b00001111; // keep only low 4 bits

assuming int8 holds the initial two 4 bit ints and is of type uint8_t and you want 2 unsigned 4bit ints.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70