简体   繁体   中英

How to convert an integer to a list of bits?

I'm trying to represent an integer as a list of bits and left pad it to 8 bits only if the integer is < 128 :

Example input: 0x15
Desired output: [0, 0, 0, 1, 0, 1, 0, 1]

I do it in the following way:

input = 0x15
output = deque([int(i) for i in list(bin(input))[2:]])
while len(output) != 8:
    output.appendleft(0)

I would like to convert any integer to a binary-list. Pad to 8 only if the number requires less than 8 bits to represent.

Another Example input: 0x715
Desired output: [1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1]

How can I do this both for numbers less then 8 bits and also for larger ones?

For a fixed size of 8 bits:

num = 0x15
out = [1 if num & (1 << (7-n)) else 0 for n in range(8)]

The (1 << (7-n)) creates a single bit mask for a given position, and then bitwise & tests to see if that bit is set in the number. Having n work through 0 to 7 results in all 8 bits in the byte being tested in order.

For arbitrarily sized numbers:

import math
num = 0x715
bits = int(max(8, math.log(num, 2)+1))
out = [1 if num & (1 << (bits-1-n)) else 0 for n in range(bits)]
>>> [int(n) for n in bin(0x15)[2:].zfill(8)]
[0, 0, 0, 1, 0, 1, 0, 1]

The slice [2:] is to remove 0b prefix, zfill(8) is to pad zeros on the left.

Solution

Works for any number of bits, faster than the accepted answer and the current highest voted answer :

num = 0xAAAA
bit_list = [(num >> shift_ind) & 1
             for shift_ind in range(num.bit_length())] # little endian
bit_list.reverse() # big endian
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]

Timings

from timeit import timeit

def accepted_answer(number):
    output = [int(x) for x in '{:08b}'.format(number)]

    return output

def highest_voted_answer(num):
    out = [1 if num & (1 << (7-n)) else 0 for n in range(8)]

    return out

def this_answer(num):
    bit_list = [(num >> shift_ind) & 1
                for shift_ind in range(num.bit_length())] # little endian
    bit_list.reverse() # big endian

    return bit_list

NUM = 0x15
ITERATIONS = int(1e7)

print(timeit(lambda: accepted_answer(NUM), number=ITERATIONS))
print(timeit(lambda: highest_voted_answer(NUM), number=ITERATIONS))
print(timeit(lambda: this_answer(NUM), number=ITERATIONS))
9.884788331000891
9.262861715000327
6.484631327999523
input = 0x15

output = [int(x) for x in '{:08b}'.format(input)]

{0:0=8b}'.format(0x15) represents your input in binary format with 0 padding to 8 digits, then using list comprehension to create a list of bits.

Alternatively, you can use map function:

output = map(int, [x for x in '{:08b}'.format(0x15)])

EDIT: Variable bit width,

If you want to make number of bits variable, here is one way:

width = 8 #8bit width
output = [int(x) for x in '{:0{size}b}'.format(0x15,size=width)]
output = map(int, [x for x in '{:0{size}b}'.format(0x15,size=width)])

This was tested in Python 2.7

It's easy to do this with format strings

>>> "{:08b}".format(0x15)
'00010101'
>>> "{:08b}".format(0x151)
'101010001'
>>> "{:08b}".format(0x1511)
'1010100010001'

to convert to a list

>>> [1 if x=='1' else 0 for x in "{:08b}".format(0x15)]
[0, 0, 0, 1, 0, 1, 0, 1]
>>> [1 if x=='1' else 0 for x in "{:08b}".format(0x1511)]
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]

It's likely to be faster using bit twiddling as in @Amber's answer, but then you'll have to check for special cases and end up with quite a bit of code. If utmost performance isn't required, it's safer to build on what you know already works

np.unpackbits(np.frombuffer(number, np.dtype('B')))

You can shift the number x steps to the right and then make a bitwise and the result with 1 to get the bit at position x , do this with list-comprehension and you get your list. If you need to support negative numbers we may need to add a leading zero to the list to ensure that positive numbers doesn't start with a 1:

import math

def bits(n):
    # The number of bits we need to represent the number
    num_bits = max(8, int(math.log(abs(n), 2)) + 1)
    # The bit representation of the number
    bits = [ (n >> i) & 1 for i in range(num_bits) ]
    bits.reverse()
    # Do we need a leading zero?
    if n < 0 or bits[0] == 0:
        return bits
    return [0] + bits

# Examples
for n in (-0x15, 0x15, 128, 255, 256, -256):
    print("{: 4} = {}".format(n, bits(n)))
  -21 = [1, 1, 1, 0, 1, 0, 1, 1] 21 = [0, 0, 0, 1, 0, 1, 0, 1] 128 = [0, 1, 0, 0, 0, 0, 0, 0, 0] 255 = [0, 1, 1, 1, 1, 1, 1, 1, 1] 256 = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] -256 = [1, 0, 0, 0, 0, 0, 0, 0, 0] 
from math import ceil
input = 0x15
bin_string = bin(input)[2:]
binary = map(int,bin_string.zfill(int(ceil(len(bin_string)/8.0)*8)))
print(binary)

This will round to nearest multiple of 8 , if u want to round to multiple of 8 only if <128, use a simple if else statement and remove zfill in else

Output for 0x15:

[0, 0, 0, 1, 0, 1, 0, 1]

Output for 0x715:

[0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1]

If you only want to add zeros if less than 128, use this:

input = 0x715
bin_string = bin(input)[2:]
num_bits = (8 if input < 128 else 0)
binary = map(int,bin_string.zfill(num_bits))
print(binary)

Ouput for 0x15:

[0, 0, 0, 1, 0, 1, 0, 1]

Output for 0x715:

[1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1]

I use bitmasks far less than I used to, but when I do use them I often have the need to decompose a value into its parts

def get_Bits(number):
i = 1
list_of_hex = []
while (i <= number):
    if ((number & i) > 0):
        bitRepresentation = hex(i)
        list_of_hex.append(bitRepresentation)
        
    i = i*2

return list_of_hex

you can change the (hex) function to (bin) if you need the binary decomposition

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM