Home > Net >  2 int8_t's to uint16_t and back
2 int8_t's to uint16_t and back

Time:10-11

I want to support some serial device in my application. This device is used with another program and I want to interact with both the device and the save files this program creates.

Yet for some yet to be discovered reason, weird integer casting is going on. The device returns uint8's over a serial USB connection, the program saves them as int8 to a file and when you read the file, you need to combine 2 int8's to a single uint16.

So when writing the save-file after reading the device, i need to convert an int8 to uint8, resulting in any value higher then 127 to be written as a negative.

Then when I read the save file, I need to combine 2 int8's into a single uint16. (So convert the negative value to positive and then stick them together)

And then when I save to a save file from within my application, I need to split my uint16 into 2 int8's.

I need to come up with the functions "encode", "combine" and "explode"

// When I read the device and write to the save file:
uint8_t val_1 = 255;
int8_t val_2 = encode(val_1);
REQUIRE(-1 == val_2);

// When I read from the save file to use it in my program.
int8_t val_1 = 7;
int8_t val_2 = -125;
uint16_t val_3 = combine(val_1, val_2);
REQUIRE(1923 == val_3);

// When I export data from my program to the device save-file
int8_t val_4;
int8_t val_5;
explode(val_3, &val_1, &val_2);
REQUIRE(7 == val_4);
REQUIRE(-125 == val_5);

Can anyone give me a head start here?

CodePudding user response:

Your encode method can just be an assignment. Implicit conversion between unsigned integer types and signed integer types is well defined.

uint8_t val_1 = 255;
int8_t val_2 = val_1;
REQUIRE(-1 == val_2);

As for combine - you'll want to cast your first value to a uint16_t to ensure you have enough bits available, and then bitshift it left by 8 bits. This causes the bits from your first value to make up the 8 most significant bits of your new value (the 8 least significant bits are zero). You can then add your second value, which will set the 8 least significant bits.

uint16_t combine(uint8_t a, uint8_t b) {
    return ((uint16_t)a << 8)   b;
}

Explode is just going to be the opposite of this. You need to bitshift right 8 bits to get the first output value, and then just simply assign to get the lowest 8 bits.

void explode(uint16_t from, int8_t &to1, int8_t &to2) {
    // This gets the lowest 8 bits, and implicitly converts
    // from unsigned to signed
    to2 = from;

    // Move the 8 most significant bits to be the 8 least
    // significant bits, and then just assign as we did
    // for the other value
    to1 = (from >> 8);
}

As a full program:

#include <iostream>
#include <cstdint>

using namespace std;

int8_t encode(uint8_t from) {
    // implicit conversion from unsigned to signed
    return from;
}

uint16_t combine(uint8_t a, uint8_t b) {
    return ((uint16_t)a << 8)   b;
}

void explode( uint16_t from, int8_t &to1, int8_t &to2 ) {
    to2 = from;
    to1 = (from >> 8);
}

int main() {
    uint8_t val_1 = 255;
    int8_t val_2 = encode(val_1);
    assert(-1 == val_2);

    // When I read from the save file to use it in my program.
    val_1 = 7;
    val_2 = -125;
    uint16_t val_3 = combine(val_1, val_2);
    assert(1923 == val_3);

    // When I export data from my program to the device save-file
    int8_t val_4;
    int8_t val_5;
    explode(val_3, val_4, val_5);
    assert(7 == val_4);
    assert(-125 == val_5);
}

For further reading on bit-manipulation mechanics, you could take a look here.

  • Related