Efficient Bit addressing in Python.

M

Mensanator

Is there a canonical way to address the bits in a structure
like an array or string or struct?

Or alternatively, is there a good way to combine eight
ints that represent bits into one of the bytes in some
array or string or whatever?

It seems to me that there is a dilemma here :

if you can write:

bit3 = 1

Then you have to jump through hoops to get
bit0 through bit7 into some byte that you can send
to an i/o routine.

On the other hand, if you keep the bits "in" the
byte, then you can write:

byte[3] = '\x7e'

but you have to jump through hoops to get at
the individual bits.

Is there a "best" way?

It would be nice to be able to write:

if io.byte2.bit3:
   do_something()

if io.byte2 == alarm_value:
  do_something_else()

where:

 io.byte2 & 8   "is"  io.byte2.bit3

Is this possible?

- Hendrik

I use the gmpy module for all my bit related work and
have been very satisfied with the results.

Examples of functions pertinent to bit operations:

digits(...)
digits(x[,base]): returns Python string representing x in the
given base (2 to 36, default 10 if omitted or 0); leading '-'
present if x<0, but no leading '+' if x>=0. x must be an mpz,
or else gets coerced into one.

getbit(...)
getbit(x,n): returns 0 or 1, the bit-value of bit n of x;
n must be an ordinary Python int, >=0; x is an mpz, or else
gets coerced to one.

hamdist(...)
hamdist(x,y): returns the Hamming distance (number of bit-
positions
where the bits differ) between x and y. x and y must be mpz,
or else
get coerced to mpz.

lowbits(...)
lowbits(x,n): returns the n lowest bits of x; n must be an
ordinary Python int, >0; x must be an mpz, or else gets
coerced to one.

numdigits(...)
numdigits(x[,base]): returns length of string representing x
in
the given base (2 to 36, default 10 if omitted or 0); the
value
returned may sometimes be 1 more than necessary; no provision
for any 'sign' characte, nor leading '0' or '0x' decoration,
is made in the returned length. x must be an mpz, or else
gets
coerced into one.

popcount(...)
popcount(x): returns the number of 1-bits set in x; note that
this is 'infinite' if x<0, and in that case, -1 is returned.
x must be an mpz, or else gets coerced to one.

scan0(...)
scan0(x, n=0): returns the bit-index of the first 0-bit of x
(that
is at least n); n must be an ordinary Python int, >=0. If no
more
0-bits are in x at or above bit-index n (which can only happen
for
x<0, notionally extended with infinite 1-bits), None is
returned.
x must be an mpz, or else gets coerced to one.

scan1(...)
scan1(x, n=0): returns the bit-index of the first 1-bit of x
(that
is at least n); n must be an ordinary Python int, >=0. If no
more
1-bits are in x at or above bit-index n (which can only happen
for
x>=0, notionally extended with infinite 0-bits), None is
returned.
x must be an mpz, or else gets coerced to one.

setbit(...)
setbit(x,n,v=1): returns a copy of the value of x, with bit n
set
to value v; n must be an ordinary Python int, >=0; v, 0 or !
=0;
x must be an mpz, or else gets coerced to one.
 
H

Hendrik van Rooyen

Is there a canonical way to address the bits in a structure
like an array or string or struct?

Or alternatively, is there a good way to combine eight
ints that represent bits into one of the bytes in some
array or string or whatever?

It seems to me that there is a dilemma here :

if you can write:

bit3 = 1

Then you have to jump through hoops to get
bit0 through bit7 into some byte that you can send
to an i/o routine.

On the other hand, if you keep the bits "in" the
byte, then you can write:

byte[3] = '\x7e'

but you have to jump through hoops to get at
the individual bits.

Is there a "best" way?

It would be nice to be able to write:

if io.byte2.bit3:
do_something()

if io.byte2 == alarm_value:
do_something_else()

where:

io.byte2 & 8 "is" io.byte2.bit3

Is this possible?

- Hendrik
 
R

Ross Ridge

Hendrik van Rooyen said:
Is there a canonical way to address the bits in a structure
like an array or string or struct?

Or alternatively, is there a good way to combine eight
ints that represent bits into one of the bytes in some
array or string or whatever?

This is the code I use to convert large bit arrays to byte strings and
back:

import string
import binascii
import array

_tr_16 = string.maketrans("0123456789abcdef",
"\x00\x01\x02\x03"
"\x10\x11\x12\x13"
"\x20\x21\x22\x23"
"\x30\x31\x32\x33")
_tr_4 = string.maketrans("0123",
"\x00\x01"
"\x10\x11")
_tr_2 = string.maketrans("01", "\x00\x01")

def string_to_bit_array(s):
"""Convert a string to an array containing a sequence of bits."""
s = binascii.hexlify(s).translate(_tr_16)
s = binascii.hexlify(s).translate(_tr_4)
s = binascii.hexlify(s).translate(_tr_2)
a = array.array('B', s)
return a

_tr_rev_2 = string.maketrans("\x00\x01", "01")
_tr_rev_4 = string.maketrans("\x00\x01"
"\x10\x11",
"0123")
_tr_rev_16 = string.maketrans("\x00\x01\x02\x03"
"\x10\x11\x12\x13"
"\x20\x21\x22\x23"
"\x30\x31\x32\x33",
"0123456789abcdef")
def bit_array_to_string(a):
"""Convert an array containing a sequence of bits to a string."""
remainder = len(a) % 8
if remainder != 0:
a.fromlist([0] * (8 - remainder))
s = a.tostring()
s = binascii.unhexlify(s.translate(_tr_rev_2))
s = binascii.unhexlify(s.translate(_tr_rev_4))
return binascii.unhexlify(s.translate(_tr_rev_16))

I don't think you can do anything faster with standard modules, although
it might not be effecient if you're only working with a single byte.

Ross Ridge
 
A

Aaron \Castironpi\ Brady

Is there a canonical way to address the bits in a structure
like an array or string or struct?

Or alternatively, is there a good way to combine eight
ints that represent bits into one of the bytes in some
array or string or whatever?

It seems to me that there is a dilemma here :

if you can write:

bit3 = 1

Then you have to jump through hoops to get
bit0 through bit7 into some byte that you can send
to an i/o routine.

On the other hand, if you keep the bits "in" the
byte, then you can write:

byte[3] = '\x7e'

but you have to jump through hoops to get at
the individual bits.

Is there a "best" way?

It would be nice to be able to write:

if io.byte2.bit3:
   do_something()

if io.byte2 == alarm_value:
  do_something_else()

where:

 io.byte2 & 8   "is"  io.byte2.bit3

Is this possible?

- Hendrik

This is tolerable. If you've got a better 'clear' operation than
'xor', you're welcome to it.

class BitSet:
def __init__( self, value ):
self.value= value
def __setitem__( self, index, value ):
if value:
self.value= self.value| (1<< index)
elif self[ index ]:
self.value= self.value^ (1<< index)
def __getitem__( self, index ):
return self.value& (1<< index )
def __repr__( self ):
return repr( self.value )

if __name__== '__main__':
b= BitSet( 15 )
print b
b[0]= 0
print b
b[0]= 1
print b
b[4]= 1
print b
b[4]= 0
print b

/Output:
15
14
15
31
15
 
A

Aaron \Castironpi\ Brady

Is there a canonical way to address the bits in a structure
like an array or string or struct?
Or alternatively, is there a good way to combine eight
ints that represent bits into one of the bytes in some
array or string or whatever?
snip

class BitSet:
    def __init__( self, value ):
        self.value= value
    def __setitem__( self, index, value ):
        if value:
            self.value= self.value| (1<< index)
        elif self[ index ]:
            self.value= self.value^ (1<< index)
    def __getitem__( self, index ):
        return self.value& (1<< index )
snip

This could read:

def __getitem__( self, index ):
return 1 if self.value& (1<< index ) else 0

Or you could shift self.value, and mask with unity.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,077
Latest member
SangMoor21

Latest Threads

Top