scruffaluff
scruffaluff

Reputation: 365

More Pythonic Way to Convert 24 bit ints to 32 bit ints

I currently use the following code to convert an array of bytes which represent little endian signed 24 bit integers into a list of signed 32 bit integers.

xs = list(bytes_object)
ys = [xs[x:x+3] + [255] if xs[x+2] & 128 else xs[x:x+3] + [0]
      for x in range(0, len(xs), 3)]
int32s = [struct.unpack('<i', bytes(y))[0] for y in ys]

Is there a more pythonic or efficient way to handle the conversion?

Upvotes: 4

Views: 2736

Answers (3)

Ian Lee
Ian Lee

Reputation: 31

Here are my solutions. -8388608 -> FF80 0000(32bit) or FFFF FFFF FF80 0000(64bit) does the magic to convert the signed values.

test_bytes = b'\x58\x18\x85'
def byte3toint(tmp, signed=True):
    b = tmp[0] | tmp[1] << 8 | tmp[2] << 16 # restore the bit represention
    if signed and tmp[2] & 128:
        b |= -8388608
    return b
assert byte3toint(test_bytes) == int.from_bytes(test_bytes, 'little', signed=True)

Upvotes: 0

James
James

Reputation: 2834

outside of numpy, this is pretty pythonic:

bytes_object = b'\x01\x00\x00\x00\x00\xf0'
[int.from_bytes(bytes_object[x:x+3], byteorder='little', signed=True) for x in range(0, len(bytes_object), 3)]

Upvotes: 2

abarnert
abarnert

Reputation: 366053

Of the top of my head, something like this:

import numpy as np

# First convert the buffer to an array of 3-vectors of uint8s
a3 = np.frombuffer(x, dtype=np.uint8).reshape(-1, 3)

# Now sign-extend it to an array of 4-vectors of uint8s
signs = (a3[..., 0] > 0x80) * 0xFF
a4 = np.concatenate((signs.reshape(-1, 1), a), axis=1)

# Now convert that to an array of int32s
i = a4.view(np.int32)

# And what you eventually wanted to do was convert to float64?
f = i.astype(np.float64)

I'm sure I've made at least one mistake (surely this won't work on a big-endian system, at least), and I don't have a computer with numpy installed in front of me, but hopefully that gets you started. No need to do anything in a Python loop.

Upvotes: 3

Related Questions