Hulio-G
Hulio-G

Reputation: 41

Converting 24 bit integer (2s complement) to 32 bit integer in C++

The dataFile.bin is a binary file with 6-byte records. The first 3 bytes of each record contain the latitude and the last 3 bytes contain the longitude. Each 24 bit value represents radians multiplied by 0X1FFFFF

This is a task I've been working on. I havent done C++ in years so its taking me way longer than I thought it would -_-. After googling around I saw this algorthim which made sense to me.

int interpret24bitAsInt32(byte[] byteArray) {     
 int newInt = (  
     ((0xFF & byteArray[0]) << 16) |  
     ((0xFF & byteArray[1]) << 8) |   
     (0xFF & byteArray[2])  
   );  
 if ((newInt & 0x00800000) > 0) {  
   newInt |= 0xFF000000;  
 } else {  
   newInt &= 0x00FFFFFF;  
 }  
return newInt;  
}  

The problem is a syntax issue I am restricting to working by the way the other guy had programmed this. I am not understanding how I can store the CHAR "data" into an INT. Wouldn't it make more sense if "data" was an Array? Since its receiving 24 integers of information stored into a BYTE.

double BinaryFile::from24bitToDouble(char *data) {
    int32_t iValue;

    // ****************************
    // Start code implementation
    // Task: Fill iValue with the 24bit integer located at data.
    // The first byte is the LSB.
    // ****************************
//iValue += 
    // ****************************
    // End code implementation
    // ****************************
    return static_cast<double>(iValue) / FACTOR;
}

bool BinaryFile::readNext(DataRecord &record)
{
    const size_t RECORD_SIZE = 6;
    char buffer[RECORD_SIZE];
    m_ifs.read(buffer,RECORD_SIZE);
    if (m_ifs) {
        record.latitude = toDegrees(from24bitToDouble(&buffer[0]));
        record.longitude = toDegrees(from24bitToDouble(&buffer[3]));
        return true;
    }
    return false;
}

double BinaryFile::toDegrees(double radians) const
{
    static const double PI = 3.1415926535897932384626433832795;
    return radians * 180.0 / PI;
}

I appreciate any help or hints even if you dont understand a clue or hint will help me alot. I just need to talk to someone.

Upvotes: 4

Views: 13445

Answers (3)

Panto Ph
Panto Ph

Reputation: 1

    int32_t upperByte   = ((int32_t) dataRx[0] << 24);
    int32_t middleByte  = ((int32_t) dataRx[1] << 16);
    int32_t lowerByte   = ((int32_t) dataRx[2] << 8);

    int32_t ADCdata32 = (((int32_t) (upperByte | middleByte | lowerByte)) >> 8);     // Right-shift of signed data maintains signed bit

Upvotes: -2

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726599

I am not understanding how I can store the CHAR "data" into an INT.

Since char is a numeric type, there is no problem combining them into a single int.

Since its receiving 24 integers of information stored into a BYTE

It's 24 bits, not bytes, so there are only three integer values that need to be combined.

An easier way of producing the same result without using conditionals is as follows:

int interpret24bitAsInt32(byte[] byteArray) {     
    return (  
        (byteArray[0] << 24)
    |   (byteArray[1] << 16)
    |   (byteArray[2] << 8)
    ) >> 8;  
}

The idea is to store the three bytes supplied as an input into the upper three bytes of the four-byte int, and then shift it down by one byte. This way the program would sign-extend your number automatically, avoiding conditional execution.

Note on portability: This code is not portable, because it assumes 32-bit integer size. To make it portable use <cstdint> types:

int32_t interpret24bitAsInt32(const std::array<uint8_t,3> byteArray) {
    return (  
        (const_cast<int32_t>(byteArray[0]) << 24)
    |   (const_cast<int32_t>(byteArray[1]) << 16)
    |   (const_cast<int32_t>(byteArray[2]) << 8)
    ) >> 8; 
}

It also assumes that the most significant byte of the 24-bit number is stored in the initial element of byteArray, then comes the middle element, and finally the least significant byte.

Note on sign extension: This code automatically takes care of sign extension by constructing the value in the upper three bytes and then shifting it to the right, as opposed to constructing the value in the lower three bytes right away. This additional shift operation ensures that C++ takes care of sign-extending the result for us.

Upvotes: 16

xvan
xvan

Reputation: 4855

When an unsigned char is casted to an int the higher order bits are filled with 0's

When a signed char is casted to a casted int, the sign bit is extended. ie:

int x;
char y;
unsigned char z;
y=0xFF
z=0xFF
x=y;
/*x will be 0xFFFFFFFF*/
x=z;
/*x will be 0x000000FF*/

So, your algorithm, uses 0xFF as a mask to remove C' sign extension, ie

0xFF == 0x000000FF
0xABCDEF10 & 0x000000FF == 0x00000010

Then uses bit shifts and logical ands to put the bits in their proper place.

Lastly checks the most significant bit (newInt & 0x00800000) > 0 to decide if completing with 0's or ones the highest byte.

Upvotes: 0

Related Questions