Лучший способ перезаписать некоторые биты в определенном диапазоне - PullRequest
5 голосов
/ 05 августа 2010

Учитывая последовательность битов, как лучше всего перезаписать определенный диапазон из них.

Например, дано:

0100 1010

Скажем, я хочу перезаписать 2 средних бита на 10, чтобы получить результат:

0101 0010

Каков наилучший способ сделать это?

Сначала я подумал, что просто переместу нужные биты перезаписи на правильную позицию (10000), а затем использую побитовое ИЛИ. Но я понял, что, хотя он сохраняет другие биты, нет способа указать, какие биты я хочу перезаписать.

Я изучал модуль bitarray в Python, но я просто хочу еще раз проверить, что я не просматриваю чрезвычайно простую побитовую операцию, чтобы сделать это для меня.

Спасибо.

Ответы [ 6 ]

7 голосов
/ 05 августа 2010

Это делается путем маскировки битов, которые вы хотите стереть (обнуляя их, сохраняя остальные биты) перед применением побитового ИЛИ.

Используйте побитовое И с шаблоном (в данном случае) 11100111.

Если у вас уже есть «положительная» версия шаблона (здесь это будет 00011000), которую легче сгенерировать, вы можете получить «отрицательную» версию 11100111, используя то, что называется дополнением 1, доступно как ~ в Python и большинстве языков с C-подобным синтаксисом.

6 голосов
/ 05 августа 2010
a = 0b01001010
a &= 0b11100111
a |= 0b00010000

and сначала обнуляет целевые биты:

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

, затем or заполняет их требуемыми данными:

  01000010
| 00010000
  --------
  01010010
4 голосов
/ 05 августа 2010

Я вижу, вы получили отличные ответы в духе "манипуляции с битами". Однако, если вам придется многое сделать, я бы по-прежнему рекомендовал прочитать обсуждение здесь и ссылки на него, и, как предполагает эта вики, несколько пакетов, найденных таким образом (BitVector, BitPacket, bitarray) - удобочитаемость важна, и опыт показывает, что удаление неочевидного встроенного кода из потока на уровне приложения в пользу обращений к хорошо названным API-интерфейсам улучшает его.

Если вы решите пойти с манипуляциями, автоматическая генерация масок битовых диапазонов с учетом битовых индексов, безусловно, имеет решающее значение. Я бы порекомендовал начать с «атомарной битовой маски» с одним битом, построенным путем сдвига:

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

как видите, 1 << 7 имеет единицу, за которой следуют 7 нулей - и, следовательно,

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

(1 << 7) - 1 имеет ровно 7 единиц (вам нужны круглые скобки, потому что приоритет оператора сдвига << ниже, чем у оператора вычитания -) в качестве младших значащих битов, иначе говоря, LSB (и, конечно, всех нулей) осталось от этого).

Учитывая простой способ сделать "битовую маску с N единицами" (как LSB), сделать единицы с битами N, включенными в набор исключенных M, и все другие очищенные, легко - используя именованные функции для дополнительной читаемости:

>>> 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'

Итак, теперь, чтобы установить биты N, включенные в M, исключенные по определенной схеме, как показали другие ответы:

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

Хотя этот ответ сам по себе не позволяет вам делать что-то большее, чем другие, я надеюсь, что он поможет «научить вас ловить рыбу» (вместо того, чтобы просто дать вам рыбу ;-) и тем самым помочь вам ( и другие читатели) в будущем.

Кстати, я бы рекомендовал свести к минимуму сочетание битовых и арифметических операций - в этой А единственная арифметическая операция, которую я использую, заключается в том, что - 1 в n_lsb, надеюсь, очень ясный случай; в более общих случаях арифметика с двумя дополнениями (как выглядит обычная целочисленная арифметика при взгляде с побитовой точки зрения) может быть хитрой.

2 голосов
/ 05 августа 2010

Вы можете сделать это в 2 шага:

  1. Создать число со всеми 0, которое вы хотите записать, которое выглядит как 1111 0111 и использовать AND
  2. Создайте число со всеми 1, которое выглядит как 0001 0000, и используйте OR

. Кстати: первое число можно легко создать, вычтя 1, сдвинутую в положение ваших нулей из 255Так, например, выполните 255 - (0000 1000).

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

, где x и y - позиции ваших соответствующих битов.

1 голос
/ 05 августа 2010
>>> bin(0b01001010 & 0b11110111 | 0b00010000)
'0b1010010'
0 голосов
/ 05 июня 2019

Это небольшой базовый набор побитовых функций, который я сделал из пары ответов (спасибо @Alex Martelli за отличный ответ).Просто откройте новый файл Python, назовите его BITManipulation.py, скопируйте этот код в него, импортируйте его из своего кода, и вы сможете получить доступ ко всем функциям без необходимости работы с битами.Во всяком случае, я всегда предлагаю понять, как это работает глубоко.

    """
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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...