Best way to overwrite some bits in a particular range
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.