Каковы правила для битовых масок? Как 0xFF против 0xFC

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

В этом примере он использует битовую маску для извлечения деталей из целого числа для каждой плитки. и целое число разбивается вот так

0xLLSDRRET

L - is the Level Number
S - Denotes a special tile(Like Stairs)
D - is if its a door, and what type(Door, Arch, Trapped)
R - Room number
E - Flags an entrance to a room
T - Names the type of tile(Floor, Cooridor, Blocked)

В этом Он использует битовую маску, чтобы получить, например, номер комнаты, такой как:

int[][] map = new int[40][40] 
int $ROOM_ID = 0x0000FF00;
System.out.println(map[x][y] & $ROOM_ID);

Теперь с этим, если map[x][y] был, например, 0x00001200, результат был бы 1200. Эту часть масок я понимаю.

Но в источнике $ROOM_ID является ACTUALLY 0x0000FFC0, и я не понимаю, что делает C, потому что я пробовал разные значения и не могу понять, что делает C, например

0x00001200 output-> 1200
0x00001210 output-> 1200
0x00001220 output-> 1200
0x00001230 output-> 1200
0x00001240 output-> 1240
0x00001250 output-> 1240
0x00001260 output-> 1240
0x00001270 output-> 1240
0x00001280 output-> 1280
0x00001290 output-> 1280
0x000012A0 output-> 1280
0x000012B0 output-> 1280
0x000012C0 output-> 12C0
0x000012D0 output-> 12C0
0x000012E0 output-> 12C0
0x000012F0 output-> 12C0

Может кто-то с большим знанием битовых масок объяснить, почему 0x0000FFC0 & 0x000012F0 = 12C0?

3 ответа

Решение

То, что вы делаете, является побитовой арифметикой. Пока игнорируйте старшие биты (так как все они равны 0) и просто рассмотрите два шестнадцатеричных значения 0xFFC0 а также 0x12F0, Затем bitwise and работает точно так же, как умножение в базе 10. На битовом уровне это будет выглядеть так:

0xFFC0 = 1111111111100000

&

0x12F0 = 0001001011110000

Это тогда равно 0001001011100000 = 0x12F0

Хитрость в преобразовании в и из шестнадцатеричного двоичного кода заключается в следующем Каждые две шестнадцатеричные цифры - это байт (т.е. 8 бит). Например, 0xFF это один байт. Таким образом, вы можете преобразовать это в его двоичное представление, просто записав значение бита для каждой шестнадцатеричной цифры (т.е. 0xF (base-16) = 1111 (base-2) = 15 (base-10)). Поскольку мы знаем, что каждый байт всегда точно 8-битный, каждая шестнадцатеричная цифра преобразуется в свое собственное 4-битное двоичное представление. Тогда вам нужно только запомнить двоичные представления для шестнадцатеричных значений 0000 (0) до 1111 (F) и заменить их соответствующим образом. Трюк работает в обоих направлениях.

Что касается битовых масок, это просто полезно для извлечения значений из битового вектора. Битовый вектор - это (обычно) простой тип данных (т.е. int, char, так далее.). Затем каждый конкретный бит обозначает тип значения для включения или отключения. Поэтому, если у меня есть битвектор (char = single byte, поэтому рассмотрим этот тип данных для битвектора, например) 0x01 и мой бит самого низкого порядка означает, что дверь включена, тогда у этого битового вектора есть дверь. Если значение моего битвектора 0x02то дверь не включена (но в 0x03 есть дверь). Почему это? Вам необходимо всегда смотреть на лежащее в основе двоичное представление, чтобы полностью понять битовый вектор / битовую маску.

0x01 = 00000001, 0x02 = 00000010, 0x03 = 00000011

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

Затем обратите внимание, что соответствующая битовая маска (по совпадению) для извлечения двери из битового вектора, отформатированного как указано выше, будет 0x01 поскольку 0x01 & 0x01 = 1, 0x02 & 0x01 = 0, а также 0x03 & 0x01 = 1 (снова вернитесь к двоичному представлению и умножьте)

Обратите внимание на двоичное, а не только на шестнадцатеричное. C в шестнадцатеричном виде 12 в десятичном виде 1100 в двоичном формате; F это 1111 в двоичном Так

F & 1 == 1, F & 2 == 2, C & F == C, 0 & 0 == 0.

0xF это все 1 биты: 1111, Побитовое И любого значения и 0xF это то же значение. Другими словами, И с 0xF в данной позиции сохраняет биты из другого операнда.

Так в 0x0000FFC0 & 0x000012F0, F цифры сохраняют соответствующие цифры:

0xF & 0x1 = 0x1
0xF & 0x2 = 0x2
0xC & 0xF = 0xC

Так же:

0xFFC0 & 0x12F0 = 0x12C0

Как вы, вероятно, знаете, побитовое И любого значения и 0 является 0, Так что конструкции вроде 0x0000FF00 используются для сохранения только второго младшего байта.

Использование 0xFFC0 на самом деле зависит от того, как остальной код использует этот младший байт. При объяснении флагов верхняя половина младшего байта задокументирована как: вход в комнату. Как-то это важно в System.out.println(map[x][y] & $ROOM_ID);,

0xC является 1100 в двоичном, так что в том числе 0xC0 в 0xFFC0 Маска, код также пытается сохранить два старших бита младшего байта. Обратите внимание, что в этих двух битах вы можете кодировать ровно 4 значения. Это соответствует игровому дизайну с возможными входами на 4 точки компаса, если игра 2D и каждая комната квадратная, в стиле Zork.

Что касается правил для битовых масок, вы можете сделать хуже, чем начинать со статьи в вики: она довольно хорошо описывает основы. В конечном итоге это все булева алгебра.

Другие вопросы по тегам