Oskyk
Oskyk

Reputation: 448

Convert from 8 bits array to 5 bits array

Is there easy way for python to convert from 8 bits to 5 bits. Currently I am using this code to do it:

def convertbits(data, frombits, tobits, pad=True):
    acc = 0
    bits = 0
    ret = []
    maxv = (1 << tobits) - 1
    max_acc = (1 << (frombits + tobits - 1)) - 1
    for value in data:
        if value < 0 or (value >> frombits):
            return None
        acc = ((acc << frombits) | value) & max_acc
        bits += frombits
        while bits >= tobits:
            bits -= tobits
            ret.append((acc >> bits) & maxv)
    if pad:
        if bits:
            ret.append((acc << (tobits - bits)) & maxv)
    elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
        return None
    return ret

is there a better way?

Edit: output should be list of 5bit integers without loosing any data in proccess

it should work like:

>>> hello = [ord(letter) for letter in 'hello']
>>> hello
[104, 101, 108, 108, 111]
>>> convertbits(hello, 8, 5)
[13, 1, 18, 22, 24, 27, 3, 15]
>>> 

Upvotes: 3

Views: 744

Answers (1)

ForceBru
ForceBru

Reputation: 44888

Well, this is relatively memory-inefficient as it converts individual bits to strings, but it seems to work:

import itertools  

def convertbits(data, From, To):
    bits_orig = itertools.chain.from_iterable(bin(n)[2:].zfill(From) for n in data)

    chunks = iter(lambda: ''.join(itertools.islice(bits_orig, To)), '')

    return [int(n, 2) for n in chunks]


print(convertbits(b'hello', 8, 5))
print([13, 1, 18, 22, 24, 27, 3, 15])

Once you got a stream of bits of the numbers (bits_orig), it's then simple to slice this stream into chunks of equal length (chunks) (this version doesn't do padding but it's fairly simple to implement) and convert the strings of ones and zeros back to numbers.


If you're working with 8-bit numbers exclusively, here's an algorithm that's 8.5(!) times faster than the one above:

from collections import deque 

def convert8bits(data, To):
    number = int.from_bytes(data, 'big')

    ret = deque()
    th = (1 << To) - 1
    while number:
        ret.appendleft(number & th)
        number >>= To

    return ret

Upvotes: 4

Related Questions