开发者

Best way to overwrite some bits in a particular range

Given a series of bits, what's the best way to overwrite a particular range of them.

For example, given:

0100 1010

Say I want to overwrite the middle 2 bits with 10 to make the result:

0101 0010

What would be the best way of doing this?

At first, I thought I would just shift the overwriting bits I want to the correct position (10000), and then use a bitwise OR. But I realized that while it preserves the other bits, there's no way of specifying which bits I want to actually overwrite.

I was looking int开发者_运维百科o Python's bitarray module, but I just want to double-check that I'm not looking over an extremely simple bitwise operation to do this for me.

Thanks.


This is done by first masking the bits you want to erase (forcing them to zero while preserving the other bits) before applying the bitwise OR.

Use a bitwise AND with the pattern (in this case) 11100111.

If you already have a "positive" version of the pattern (here this would be 00011000), which is easier to generate, you can obtain the "negative" version 11100111 using what is called 1's complement, available as ~ in Python and most languages with a C-like syntax.


a = 0b01001010
a &= 0b11100111
a |= 0b00010000

The and first zeroes the target bits:

  01001010
& 11100111
  --------
  01000010

then the or fills them with the desired data:

  01000010
| 00010000
  --------
  01010010


I see you got excellent answers in the "bit manipulation" vein. If you have to do a lot of this, though, I would still recommend reading the discussion here and links therefrom, and, as that wiki suggests, a few of the packages found this way (BitVector, BitPacket, bitarray) -- readability is important, and experience shows that removing non-obvious inline code from your application-level flow in favor of calls to well-named APIs enhances it.

If you do decide to go with manipulation, automatic generation of the bit-ranges masks given bit-indices is clearly crucial. I would recommend starting with an "atomic bitmask" with just one 1 bit, built by shifting:

>>> bin(1 << 7)
'0b10000000'

as you see, 1 << 7 has a one followed by 7 zeros -- and therefore:

>>> bin((1 << 7) - 1)
'0b1111111'

(1 << 7) - 1 has exactly 7 ones (you need the parentheses because the priority of the shifting operator << is lower than that of the subtraction operator -) as the least significant bits aka LSB (and of course all zeros left of that).

Given an easy way to make "a bitmask with N ones" (as the LSB), making ones with bits N included to M excluded set and all other cleared is easy -- using named functions for extra readability:

>>> def n_lsb(x): return (1 << x) - 1
... 
>>> def n_to_m(n, m): return n_lsb(n) & ~ n_lsb(m)
... 
>>> bin(n_to_m(7, 3))
'0b1111000'

So, now, to set bits N included to M excluded to a certain pattern, as other answers have shown:

>>> def set_bits(i, n, m, pat): return (i & ~n_to_m(n, m))|(pat<<m)
... 
>>> bin(set_bits(511, 7, 3, 0b0101))
'0b110101111'

While this answer does not, per se, allow you to do anything more than the others, I do hope it can help "teach you to fish" (vs. just giving you a fish;-) and thereby help you (and other readers) in the future.

BTW, I would recommend reducing to a minimum the mix of bitwise and arithmetic operations -- in this A the only arithmetic operation I use is that - 1 in n_lsb, a hopefully very clear case; in more general cases, two's complement arithmetic (what ordinary integer arithmetic looks like when looked at from a bitwise point of view) could be tricky.


You can do it in 2 steps:

  1. Create a number with all the 0's you want to write in that looks like 1111 0111 and use an AND
  2. Create a number with all the 1's that looks like 0001 0000 and use an OR

BTW: The first number can easily be created by subtracting a 1 shifted into the position of your zeros from 255. So for example, do 255 - (0000 1000).

    a = 255 - (1 << x)
    b = 1 << y
    result = (source & a) | b

where x and y are the positions of your respective bits.


>>> bin(0b01001010 & 0b11110111 | 0b00010000)
'0b1010010'


This is a small basic set of bitwise functions I made from a couple of answers( Thanks to @Alex Martelli for the great answer). Just open a new python file call it BITManipulation.py copy this code to it import it from your code and you can access all the functions without any need for working with bits. Anyway I always suggest to understand how it work deeply.

    """
BitManipulation.py
This file is used to define some basic bit manipulations functions
"""

#@func: get_bit
#@params: value,n
#@return: the N-th bit from value
def get_bit(value, n):
    return ((value >> n & 1) != 0)

#@func: set_bit
#@params: value,n
#@return: value with the N-th bit Set to 1
def set_bit(value, n):
    return value | (1 << n)

#@func: clear_bit
#@params: value,n
#@return: value with the N-th bit Set to 0
def clear_bit(value, n):
    return value & ~(1 << n)

#@func: n_lsb
#@params: x ( Example 7)
#@return: a number with exactly x bits of 1 ( Example 0b1111111 )
def n_lsb(x):
    return (1 << x) - 1

#@func: n_to_m
#@params: n,m (n > m)( Example 5,3 )
#@return: a number with bits n -> m set to 1 ( Example 0b00011000 )
def n_to_m(n, m):
    return n_lsb(n) & ~ n_lsb(m)

#@func: set_bits
#@params: x,n,m,pat (n > m)( Example 511,5,3,0b0101 )
#@return: sets bits n -> m in x to pat ( Example 0b110101111 )
def set_bits(x, n, m, pat):
    return (x & ~n_to_m(n, m))|(pat<<m)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜