Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Signed extension from 24 bit to 32 bit in C++

I have 3 unsigned bytes that are coming over the wire separately.

[byte1, byte2, byte3]

I need to convert these to a signed 32-bit value but I am not quite sure how to handle the sign of the negative values.

I thought of copying the bytes to the upper 3 bytes in the int32 and then shifting everything to the right but I read this may have unexpected behavior.

Is there an easier way to handle this?

The representation is using two's complement.

like image 579
Beto Avatar asked Mar 01 '17 14:03

Beto


People also ask

What is sign extension in C?

Sign-extending means copying the sign bit of the unextended value to all bits on the left side of the larger-size value.

Is unsigned long always 32 bit?

long is guaranteed (at least) 32 bits.

What is sign extend and zero extend?

Sign extension is used for signed loads of bytes (8 bits using the lb instruction) and halfwords (16 bits using the lh instruction). Sign extension replicates the most significant bit loaded into the remaining bits. Zero extension is used for unsigned loads of bytes ( lbu ) and halfwords ( lhu ).


Video Answer


3 Answers

You could use:

uint32_t sign_extend_24_32(uint32_t x) {
    const int bits = 24;
    uint32_t m = 1u << (bits - 1);
    return (x ^ m) - m;
}

This works because:

  • if the old sign was 1, then the XOR makes it zero and the subtraction will set it and borrow through all higher bits, setting them as well.
  • if the old sign was 0, the XOR will set it, the subtract resets it again and doesn't borrow so the upper bits stay 0.

Templated version

template<class T>
T sign_extend(T x, const int bits) {
    T m = 1;
    m <<= bits - 1;
    return (x ^ m) - m;
}
like image 133
harold Avatar answered Oct 16 '22 06:10

harold


Assuming both representations are two's complement, simply

upper_byte = (Signed_byte(incoming_msb) >= 0? 0 : Byte(-1));

where

using Signed_byte = signed char;
using Byte = unsigned char;

and upper_byte is a variable representing the missing fourth byte.

The conversion to Signed_byte is formally implementation-dependent, but a two's complement implementation doesn't have a choice, really.

like image 36
Cheers and hth. - Alf Avatar answered Oct 16 '22 06:10

Cheers and hth. - Alf


You could let the compiler process itself the sign extension. Assuming that the lowest significant byte is byte1 and the high significant byte is byte3;

int val = (signed char) byte3;                // C guarantees the sign extension
val << 16;                                    // shift the byte at its definitive place
val |= ((int) (unsigned char) byte2) << 8;    // place the second byte
val |= ((int) (unsigned char) byte1;          // and the least significant one

I have used C style cast here when static_cast would have been more C++ish, but as an old dinosaur (and Java programmer) I find C style cast more readable for integer conversions.

like image 1
Serge Ballesta Avatar answered Oct 16 '22 08:10

Serge Ballesta