Русское сообщество по скриптингу

Работа с битами (использование операторов)

Статьи или фрагменты кода для новичков и уже опытных скриптеров по AMXX.

Модератор: Chuvi

Правила форума
1. Запрещено материться и оскорблять других участников форума.
2. Запрещен флуд, оффтоп, дабл постинг во всех разделах форума, кроме раздела "Болтовня".
3. Запрещено взламывать сайт/форум или наносить любой вред проекту.
4. Запрещено рекламировать другие ресурсы.
5. Запрещено создавать темы без информативного названия. Название темы должно отображать ее смысл.

В данном разделе форума разрешено создавать темы, касающие только обучающему материалу по AMX Mod X.

Работа с битами (использование операторов)

Сообщение DJ_WEST » 07 окт 2009, 11:32

Автор: ot_207
Перевод и редактирование: 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-= 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
-= 7-= 6
2. 1
<<(id-1) = 1<<=
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 
 
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Работа с битами (использование операторов)

Сообщение Lt.RAT » 14 ноя 2010, 13:49

Fedcomp писал(а):Разница в скорости. Именно ради скорости битсуммы и юзаются

вот уж тогда все надо на отдельных переменных...

Fedcomp писал(а):разве дефайны из инклюда работают?

:-| А как по твоему "работает" hlsdk_const.inc или fakemeta_util

Fedcomp писал(а):И потерял всю производительность. Правильно делается через #define, charsmax тому пример

Или тотже charsmax - тоже дефайн всетаки.
Аватара пользователя
Lt.RAT
 
Сообщения: 301
Зарегистрирован: 30 сен 2009, 01:44
Благодарил (а): 4 раз.
Поблагодарили: 151 раз.
Языки программирования: Counter-Strike 1.6

Re: Работа с битами (использование операторов)

Сообщение Fedcomp » 14 ноя 2010, 14:05

Lt.RAT писал(а):вот уж тогда все надо на отдельных переменных...

Хочешь сказать функции вызываются быстрее?

Lt.RAT писал(а): :-| А как по твоему "работает" hlsdk_const.inc или fakemeta_util

У меня не получалось, потому и спрашивал.

Lt.RAT писал(а):Или тотже charsmax - тоже дефайн всетаки.

А я разве не это сказал?
Не помогаю в ЛС - есть форум.
Плагины тоже не пишу, на форуме достаточно хороших скриптеров.


"я ставлю зависимости потому что мне приятно" - subb98 @ 2017
Аватара пользователя
Fedcomp
Администратор
 
Сообщения: 4936
Зарегистрирован: 28 авг 2009, 20:47
Благодарил (а): 813 раз.
Поблагодарили: 1317 раз.
Языки программирования: =>
pawn / php / python / ruby
javascript / rust

Re: Работа с битами (использование операторов)

Сообщение Lt.RAT » 14 ноя 2010, 18:02

Fedcomp писал(а):Хочешь сказать функции вызываются быстрее?

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

Lt.RAT писал(а):Или тотже charsmax - тоже дефайн всетаки.

А я разве не это сказал?[/quote]
Да вот в этом то и дело, что сам привел пример с дефайном в inc файле, а потом ставишь под сомнение что такое возможно...
Аватара пользователя
Lt.RAT
 
Сообщения: 301
Зарегистрирован: 30 сен 2009, 01:44
Благодарил (а): 4 раз.
Поблагодарили: 151 раз.
Языки программирования: Counter-Strike 1.6

Re: Работа с битами (использование операторов)

Сообщение Skriptar » 14 ноя 2010, 18:53

Lt.RAT, У тебя случайно нету unpack'a и pack'a amxx ?
Я знаю только то, что я ничего не знаю.
Аватара пользователя
Skriptar
 
Сообщения: 953
Зарегистрирован: 20 окт 2009, 15:34
Благодарил (а): 180 раз.
Поблагодарили: 136 раз.
Языки программирования: Counter-Strike 1.6

Re: Работа с битами (использование операторов)

Сообщение ejik » 07 янв 2011, 23:13

мне хотелось бы кое-что уточнить:
допустим есть число 3 (возьмем четыре бита) - 0011 (это ab при read_flags())
и если использовать
[pawn]if(get_user_flags(id/* вернет 3, т.е. ab (0011) */ read_flags(ab)/*вернет 3, 0011 */[/pawn] то должно вернуть 1, а если использовать
[pawn]
if(get_user_flags(id/* 3, ab (0011) */ read_flags(ac/* 5, 0101 */[/pawn]
то вернет 0, т.к.
Код: Выделить всё
0011
&
0101
=
0001 => 0001 != 0101 и 0001 != 0011

То есть, если возвращаемые биты не равны исходным return 0, а если равны, то 1. Правильно я думаю?
Аватара пользователя
ejik
 
Сообщения: 249
Зарегистрирован: 01 июл 2010, 14:07
Благодарил (а): 83 раз.
Поблагодарили: 83 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6

Re: Работа с битами (использование операторов)

Сообщение DJ_WEST » 08 янв 2011, 12:16

[pawn]if (get_user_flags(id) & read_flags(a)) // если у игрока "ab"  [/pawn]
0011
&
0001
=
0001
Как видишь результат равняется read_flags(a).

Более удачно рассматривать сложнее:
[pawn]if (get_user_flags(id) & read_flags(ad)) // если у игрока "abcde"  [/pawn]
a = 0000 0001
ab = 0000 0011
abc = 0000 0111
abcd = 0000 1111
abcde = 0001 1111

Следовательно:
0001 1111
&
0000 1001
=
0000 1001

Как видно результат равняется тому, что мы сравнивали, то есть read_flags(ad).
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Работа с битами (использование операторов)

Сообщение ejik » 08 янв 2011, 12:34

и везде вернет 1, тогда получается
[pawn]if(get_user_flags(id) & read_flags(cd)) // у игрока abd   [/pawn]
abd = 1011
cd = 1100
1011
&
1100
=
1000
мы не получили желаемого результата (read_flags()) значит возвращаем 0.
Аватара пользователя
ejik
 
Сообщения: 249
Зарегистрирован: 01 июл 2010, 14:07
Благодарил (а): 83 раз.
Поблагодарили: 83 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6

Re: Работа с битами (использование операторов)

Сообщение DJ_WEST » 08 янв 2011, 13:07

Да.
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Работа с битами (использование операторов)

Сообщение Lt.RAT » 09 мар 2011, 20:02

[quote=DJ_WEST]Данный оператор работает, как логический оператор ^^[/quote]
Не подскажете как пользоваться "логическим оператором ^^" ?)
Аватара пользователя
Lt.RAT
 
Сообщения: 301
Зарегистрирован: 30 сен 2009, 01:44
Благодарил (а): 4 раз.
Поблагодарили: 151 раз.
Языки программирования: Counter-Strike 1.6

Re: Работа с битами (использование операторов)

Сообщение nato » 27 дек 2012, 18:53

Вопрос следующий:
к примеру есть массив для игроков is_alive[33] игроку можно присвоить к примеру число 150000 (is_alive[id] = 150000)
возможно ли такое присвоение числа при работе с битами т.е. как-то так (1<<(id-1)) = 150000 ?
Аватара пользователя
nato
 
Сообщения: 346
Зарегистрирован: 06 июл 2011, 17:45
Благодарил (а): 30 раз.
Поблагодарили: 91 раз.
Опыт программирования: Около года
Языки программирования: Counter-Strike 1.6

Пред.След.

Вернуться в Статьи / фрагменты кода

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5