Перевод и редактирование: DJ_WEST
Данная статья поможет вам научиться работать с битами через битовые операторы, что даст вам некоторый прирост работы плагина по сравнению с булевыми массивами. Работа с битами быстрее для компьютера, но для понимания кода человеком сложнее.
Что такое битсумма?
Битсумма - это то, на чем основана работа памяти. Память состоит из ячеек (битов), которые могут хранить два значения: 1 и 0. Пример, как хранится число в одном байте (байт = 8 бит):
"8" (обычное число) = "00001000" (число в памяти [1 байт])
Как битсумма работает?
Идея состоит в том, что битсумма позволяет управлять ее позицией. И мы можем проверить имеет ли позиция 1 или 0.
Какие есть операторы и как они работают?
- Оператор |
Данный оператор работает, как логический оператор ||, но производит операцию над каждым битом.
Пример:- Код: Выделить всё
first_byte = "10010001"
second_byte = "01010001"
first_byte | second_byte = "11010001"
- Код: Выделить всё
| 1 0
1 1 1
0 1 0
- Оператор &
Данный оператор работает, как логический оператор &&, но производит операцию над каждым битом.
Пример:- Код: Выделить всё
first_byte = "10010001"
second_byte = "01010001"
first_byte & second_byte = "00010001"
- Код: Выделить всё
& 1 0
1 1 0
0 0 0
- Оператор ^ (Исключающее ИЛИ)
Данный оператор работает, как логический оператор ^^, но производит операцию над каждым битом.
Пример:- Код: Выделить всё
first_byte = "10010001"
second_byte = "01010001"
first_byte ^ second_byte = "11000000"
- Код: Выделить всё
^ 1 0
1 0 1
0 1 0
- Оператор ~
Данный оператор отрицает каждый бит, поэтому бит 0 становится 1, а бит 1 становится 0.
Пример:- Код: Выделить всё
first_byte = "10010001"
~first_byte = "01101110"
- Код: Выделить всё
~1 = 0
~0 = 1
- Оператор <<
Данный оператор смещает биты влево на указанное число позиций.
Например:- Код: Выделить всё
"00001100" << 1 = "00011000"
Если мы преобразуем полученное число в десятичную систему, то увидем, что в результате получилось число умноженное на 2. Следовательно, 1<<3 будет тоже самое, что 2^3 * 1. - Оператор >>
Данный оператор смещает биты вправо на указанное число позиций.
Например:- Код: Выделить всё
"00000111" >> 3 = "00000000"
Как работают обычные числа?
Числа хранятся в ячейках. Следовательно, числа из десятичной системы преобразуются в двоичную. К примеру, число 9 становится "00001001". Чтобы перобразовать число из двоичной системы в десятичную, нам нужно умножить каждую позицию на число 2 в степени:
00001001: 1*(2^0) + 0*(2^1) + 0*(2^2) + 1*(2^3) + 0*(2^4) + 0*(2^5) + 0*(2^6) + 0*(2^7) = 1 + 8 = 9
Как применить данную статью в моих плагинах?
Мы можем использовать битсуммы вместо булевых массивов.
Примечание: Важно отметить, что если, к примеру, у нас есть 1 = 0000 0000 0000 0000 0000 0000 0000 0001 и выполняя 1 << 32 мы получаем: 1 0000 0000 0000 0000 0000 0000 0000 0000, как видно 1 выходит за пределы. Поэтому при 32 игроках на сервере, мы не получим верную работоспособность. Для этого надо отнять 1. Вместо 1-32 у нас будет 0-31.
К примеру, вместо:
- Код: Выделить всё
new bool: is_alive[33]
Запишем:
- Код: Выделить всё
new bitsum_is_alive
Вместо:
- Код: Выделить всё
if (is_alive[id])
{
}
Запишем:
- Код: Выделить всё
if (bistum_is_alive & (1<<(id-1)))
{
}
Вместо:
- Код: Выделить всё
is_alive[id] = true
- Код: Выделить всё
is_alive[id] = false
Запишем:
- Код: Выделить всё
bitsum_is_alive |= (1<<(id-1))
- Код: Выделить всё
bitsum_is_alive &= ~(1<<(id-1))
Если рассматривать, к примеру, присвоение:
- Код: Выделить всё
is_alive[id] = true
или как мы заменили:
- Код: Выделить всё
bitsum_is_alive |= (1<<(id-1))
где id = 1 при bitsum_is_alive = 0000 0000 0000 0000 0000 0000 0000 0000, то есть игрок id = 1, первый на сервере.
Вычисления:
- Код: Выделить всё
1. id-1 = 1-1 = 0
2. 1<<(id-1) = 1 << 0 =
0000 0000 0000 0000 0000 0000 0000 0001 << 0 = 0000 0000 0000 0000 0000 0000 0000 0001
3. bitsum_is_alive | (1<<(id-1)) =
0000 0000 0000 0000 0000 0000 0000 0000
|
0000 0000 0000 0000 0000 0000 0000 0001
=
0000 0000 0000 0000 0000 0000 0000 0001
4. bitsum_is_alive = bitsum_is_alive | (1<<(id-1)) = 0000 0000 0000 0000 0000 0000 0000 0001
А если допустить, к примеру, что bitsum_is_alive = 0000 0000 0000 0010 0100 0000 0100 0001, что говорит нам о том, что игроки у которых id равно 1, 7, 15, 18 - живы, так как у них бит равен 1, отсчет битов ведется с конца. То присвоение игроку id = 7 (выделенный красный бит) статуса мертвый (бит в 0), будет происходить след. образом (как мы заменяли код выше):
- Код: Выделить всё
bitsum_is_alive &= ~(1<<(id-1))
Вычисления:
- Код: Выделить всё
1. id-1 = 7-1 = 6
2. 1<<(id-1) = 1<<6 =
0000 0000 0000 0000 0000 0000 0000 0001 << 6 = 0000 0000 0000 0000 0000 0000 0100 0000
3. ~(1<<(id-1)) = 1111 1111 1111 1111 1111 1111 1011 1111
4. bitsum_is_alive & ~(1<<(id-1)) =
0000 0000 0000 0010 0100 0000 0100 0001
&
1111 1111 1111 1111 1111 1111 1011 1111
=
0000 0000 0000 0010 0100 0000 0000 0001
5. bitsum_is_alive = bitsum_is_alive & ~(1<<(id-1)) = 0000 0000 0000 0010 0100 0000 0000 0001.
Мы получили bitsum_is_alive = 0000 0000 0000 0010 0100 0000 0000 0001
Можно ли как-то оптимизировать работу?
Можно сделать stock функцию:
- Код: Выделить всё
stock is_user_alive(id)
{
return (bistum_is_alive & (1<<(id-1))) ? 1 : 0
}
Или макрос:
- Код: Выделить всё
#define is_user_alive(%1) (bistum_is_alive & (1 << (%1-1))) ? 1 : 0