24 Bit Unsigned Integer

4

I start working with HART (Highway Addressable Remote Transducer) Protocol and I found something, let's say different and I try to understand it, more precisely the uint24. I understand that this structure uses an UInt32 internally for storage and most other common expected integer functionality, so using a 24-bit integer will not save memory. But my question is how can I convert an UInt32 to this specific type, I found these lines of code in Javascript but I don't know what is the purpose of those left shifting.

var getUint24 = function(bytes)
{
    return (DataView(bytes.buffer).getUint16(0) << 8) + DataView(bytes.buffer).getUint8(0 + 2);
}

Also, I found this function that convert a number to 4 bytes. I also don't know what is the purpose o these shifting, but maybe if I understand these I can create my own version of the function that convert a simple number ex: 4 to his uint24 version.

/**
 *
 * @function ToBytesInt32 "Convert number to 4 bytes (eg: ToBytesInt32(2) returns 00 00 00 02"
 * @param {number} num The input value
 * @return {byte array}
 */
function ToBytesInt32BigEndian(num) {
    if (isNaN(num)) {
        throw "ToBytesInt32 received Nan! Called from: " +
        testUtils.StrReplace.caller.toString().split('\n')[0];
    }
    arr = [
        (num & 0xff000000) >> 24,
        (num & 0x00ff0000) >> 16,
        (num & 0x0000ff00) >> 8,
        (num & 0x000000ff)
    ];
    return arr;
}

If anyone can help me to understand the pourpose of those shifting I would apreciate it, also the C/C++ version is very welcomed.

javascript
c++
c
byte
bit-shift
asked on Stack Overflow Nov 13, 2020 by Vesa95 • edited Nov 16, 2020 by Vesa95

1 Answer

1

Short answer

  • left << shifting is a padding of significant bits with zeros for future conjunction into single value;
  • right >> shifting is part of extracting of value's segment into smaller data type (goes after applying mask);

Abstract

Shift & sum is the way to pack/unpack values when reading/writing frame is less of value's length. Let's say we have 4 bit value: 1011. And we able read it only by 2 bit frames: [10], [11].

Let's assume that we are reading stream from left to right.

# offset 0 bits
a = stream(0)      # a = 10
a << 2             # a = 1000

b = stream (2)     # b = 11

return a + b       # 1000 + 11 = 1011

Also you should pay attention to BE/LE (Big/Little Endian). In different Endians value 1011 may be presented in stream as 10, 11 or 11, 10.

getUint24 example

In case of getUint24 example we have 24 bit value packed as sequence like [16 bit, 8 bit]. Let's draw it as [XY, Z] where single letter is 1 byte.

# offset 0 bytes
a = DataView(bytes.buffer).getUint16(0)      # a = XY
a << 8                                       # a = XY0

# offset 2 bytes
b = DataView(bytes.buffer).getUint8(2)       # b = Z

return a + b                                 # XY0 + Z = XYZ

And again bytes.buffer may keep value either as [XY, Z] or [Z, XY]. The getUintX(offset) hides this details from us. But if you'd like to write own converter, you need investigate what format is in your case.

ToBytesInt32BigEndian example

Here we see another concern of packing values - apply mask.

For say we have 32 bit value AB CD EF 12. We can apply mask to extract needed segment of the value.

For example, if we have 3 bit value 011 and would like get value of second bit we need apply &-mask where subject bits evaluated against 1 keep original value while rest bits evaluated against 0 turn any value into 0.

masked    = 011 & 010    # 010
bit_value = masked >> 1  # 001

In the same way with our ABCDEF12

x = 0xABCDEF12 & 0xFF000000   # x = 0xAB000000
x >> 24                       # x = 0x000000AB
...

This way we will get array [0xAB, 0xCD, 0xEF, 0x12]. Without shifting we were get array [0xAB000000, 0x00CD0000, 0x0000EF00, 0x00000012] which is not what we want.

answered on Stack Overflow Nov 16, 2020 by x'ES

User contributions licensed under CC BY-SA 3.0