Operatory bitowe

Niedziela, 14 Lutego 2010, 16:36
Czas czytania 5 minut, 19 sekund
Zgodne z GM: gm5 gm6 gm7 gm8 gms1 gms2
Poza zwykłymi operatorami arytmetycznymi takimi jak + i -, GM oferuje nam również operatory operujące na zapisach binarnych liczb. Ten artykuł przybliży wam ich działanie.
W komputerze każda informacja zapisywana jest przy pomocy kodu binarnego (dwójkowego) składającego się jedynie z zer i jedynek. Przykładowo liczba 2 reprezentowana jest przez następujący ciąg: 10, a liczba 5 przez: 101. Jest to dość intuicyjne, po prostu wyobraźmy sobie, że istnieją jedynie te dwie cyfry. Wtedy bo 1 następuje 10. Kolejne pierwsze liczby w zapisie binarnym wyglądają więc tak:
1: 1
2: 10
3: 11
4: 100
5: 101
6: 110
7: 111
8: 1000
itd.
Mam nadzieję, że jest to zrozumiałe. Nie będę się zagłębiał w temat zapisu binarnego, bo nie o tym ma być ten artykuł.

GameMaker poza zwykłymi operatorami arytmetycznymi takimi jak +, -, * lub / oferuje nam również operatory działające na zapisie binarnym liczb. Należą do nich:
kod[CZCIONKA=Courier]
| - OR bitowa suma logiczna (alternatywa)
& - AND bitowy iloczyn logiczny (koniunkcja)
^ - XOR bitowa różnica symetryczna
<< - przesunięcie w lewo
>> - przesunięcie w prawo
~ - negacja bitowa
[/CZCIONKA]

Poniższe tabelki przedstawiają jakie wyniki zwracają poszczególne operatory dla danych argumentów. Za moment każdy z nich zostanie dokładniej omówiony.
Grafika: upload/screens/articles/tabele_bool.PNG

[ROZMIAR=13px]| - bitowa suma logiczna (alternatywa)[/ROZMIAR]

Działa analogicznie do operatora logicznego ||. Operator ten przyjmuje jako argumenty 2 liczby. Następnie sprawdza wartości ich kolejnych bitów. Jeśli chociaż jeden z nich jest równy 1 to odpowiedni bit liczby wynikowej będzie miał wartość 1.
kod[CZCIONKA=Courier]
Przykład:
105 1101001
45 0101101
105 | 45 1101101[/CZCIONKA]


Jak widzimy jedynie 2 bity pozostały zerowe ponieważ tylko na 2 pozycjach obie liczby miały zerowy bit. Powstała w wyniku tej operacji liczba to 109.

[ROZMIAR=13px]& - bitowy iloczyn logiczny (koniunkcja)[/ROZMIAR]

Ten operator jest bitowym odpowiednikiem operatora logicznego &&. Działa bardzo podobnie jak omówiony przed chwilą operator |. Różnicą jest to, że wynikowy bit ma wartość 1 jedynie wtedy gdy odpowiednie bity obydwu liczb podanych jako argument są równe 1.
kod[CZCIONKA=Courier]
Przykład:
105 1101001
45 0101101
105 & 45 0101001[/CZCIONKA]


Powstała liczba to 41. Operator ten, może posłużyć nam do obliczenia reszty z dzielenia przez 2. Jeśli dowolną liczbę potraktujemy tym operatorem jako drugi argument podając liczbę 1 to uzyskamy właśnie resztę z dzielenia przez 2.
kod[CZCIONKA=Courier]
Przykład:
105 1101001
1 0000001
105 & 1 0000001[/CZCIONKA]

Na pierwszy rzut oka widać, że wszystkie bity poza ostatnim muszą zostać wyzerowane. Dzieje się tak dlatego, że liczba 1 ma tylko jeden bit równy 1. Wiadomo, że liczba jest podzielna przez 2 jeśli jej ostatni bit jest równy zero. Trudno się z tym nie zgodzić. Jeśli więc będzie podzielna przez 2 ostatni bit wynikowej liczby będzie równy 0, a więc cała liczba również będzie równa 0. W przeciwnym wypadku wynikiem będzie 1. Taka operacja jest znacznie szybsza od zwykłego dzielenia modulo.

[ROZMIAR=13px]^ - bitowa różnica symetryczna[/ROZMIAR]

Ten operator działa podobnie do dwóch poprzednich. Tutaj jednak wynikiem jest 1 gdy dokładnie jeden z argumentów ma wartość 1. Jest to równoważne temu, że odpowiednie bity się od siebie różnią.
kod[CZCIONKA=Courier]
Przykład:
105 1101001
45 0101101
105 ^ 45 1000100[/CZCIONKA]

Powstała liczba to 68. Operator ten ma ciekawą własność. Mianowicie operacja ta jest odwracalna. Gdybyśmy teraz liczbę 68 potraktowali operatorem ^ i jako drugi argument podali 45 to otrzymali byśmy z powrotem 105. Można wykorzystać tę własność do prostego szyfrowania danych. Wystarczy każdy bajt XORować z jakimś kluczem. By odszyfrować dane wystarczy je przeXORować z tym samym kluczem.

[ROZMIAR=13px]<< - przesunięcie w lewo[/ROZMIAR]

Operator ten również przyjmuje 2 argumenty. Jego działanie jednak znacząco różni się od przedstawionych przed chwilą trzech operatorów bitowych. Pierwszy argument to liczba poddawana operacji, drugi to wartość przesunięcia. Operator ten przesuwa wszystkie bity danej liczby o daną wartość w lewo, a w powstałych miejscach po prawej wstawia 0.
kod[CZCIONKA=Courier]
Przykład:
5 0000101
5 << 3 0101000[/CZCIONKA]
W wyniku tej operacji powstała liczba 40. Nie trudno zauważyć, że jest to nic innego jak mnożenie przez kolejne potęgi dwójki. Oczywiście przesunięcie o 30 bitów w lewo jest znacznie szybsze niż 30-krotne wymnożenie liczby przez 2.

[ROZMIAR=13px]>> - przesunięcie w prawo[/ROZMIAR]

Działanie niemal identyczne jak w przypadku poprzedniego operatora. Tutaj jednak wszystkie bity przesuwane są w prawo, a z lewej strony pozostają nam zera.
kod[CZCIONKA=Courier]
Przykład:
45 0101101
45 >> 3 0000101 [/CZCIONKA]
W wyniku powstała liczba 5. Można zauważyć, że w przypadku tego operatora kilka skrajnych bitów po prawej stronie zostaje utraconych. Dzięki temu zjawisku przesunięcie w prawo okazuje się równoważne całkowitoliczbowemu dzieleniu przez potęgi 2! Sprawdźmy to.
kod[CZCIONKA=Courier]
45 / 2 = 22.5
22 / 2 = 11
11 / 2 = 5.5

45 / 2^3 = 45 / 8 = 5.625[/CZCIONKA]
Zgadza się! Każda utracona w zapisie binarnym jedynka jest to zgubiona część po przecinku.
Dodatkowo teraz wyciągając resztę z dzielenia przez 2 możemy uzyskać wartość konkretnego bitu początkowej liczby.

[ROZMIAR=13px]~ - negacja bitowa[/ROZMIAR]

Pozostała nam do omówienia jedynie negacja bitowa. Ten operator jest wyjątkowy ponieważ jest jednoargumentowy. Liczba będąca wynikiem tej operacji jest utworzona przez zamianę wszystkich 1 w zapisie binarnym na 0, a wszystkich 0 na 1.
kod[CZCIONKA=Courier]
Przykład:
45 0101101
~45 1010010 [/CZCIONKA]
Powstała nam liczba -46. Może wam się to wydać nieco dziwne. Jest to spowodowane tym, że aktualnie większość komputerów korzysta z systemu reprezentacji liczb całkowitych U2. Nie będę dokładnie wyjaśniał na czym on polega, bo jest to materiał na nowy artykuł, ale powiem w skrócie. Każda liczba posiada jeden dodatkowy bit znajdujący się na początku i określający czy liczba jest dodatnia (0) czy ujemna (1). Tak więc w rzeczywistości dla komputera 1001 to nie jest 9, a -7. 9 natomiast wyglądałoby tak: 01001. Dzięki takiemu sposobowi zapisu negacja zyskuje pewną ciekawą właściwość. Mianowicie: ~X == (-X-1).
Nietrudno zauważyć, że negacja jest operacją odwracalną, czyli: X == ~(~X).


I to by było na tyle. Możliwe, że wielu uzna operatory bitowe za zbędne, ale w niektórych przypadkach naprawdę się przydają. Przykładowo przy implementowaniu Drzew Potęgowych przy pomocy prostej linijki x-(x&(x-1)) cała skomplikowana struktura sprowadza się do 5 linijek kodu. A nieprawdopodobne jest w jaki sposób to działa :D

Dziękuję za uwagę : )
Komentarze (łącznie 15):
gnysek (Nie., 14 Lut. 10, 19:38)
#1

Warto zauważyć, że np. mnożenie razy 10 to np. (a<<3)+(a<<1). Taka ciekawostka.
Zabrakło tabelki dla AND, OR, XOR, NOT z algebry boola :)

Platyna (Nie., 14 Lut. 10, 19:55)
#2

Słuszna uwaga! Tabelki dodane :)

gnysek (Nie., 14 Lut. 10, 19:56)
#3

Daj je na początku, przed or :)

S
Snake (Wto., 16 Lut. 10, 22:44)
#4

Dobry artykuł. Można by jeszcze wspomnieć o operatorach |=, &=, ^=, braku >>=, <<= i o tym, że GM-owy real ma 64 bity a poprawnie operować można jedynie na tych 32 mniej znaczących :P

Platyna (Wto., 16 Lut. 10, 23:22)
#5

No niestety to jest problematyczne. Mógłbym mój przykład licznika przerobić by używał własnej arytmetyki, bo się wykrzaczał na dużych liczbach, ale to by znowu początkujący nie zrozumieli i by się z celem mijało :P

Co do operatorów to jakoś mi umknęły z pamięci, bo z helpem sprawdzałem czy o niczym nie zapomniałem, a tam ich nie było.

Robert Prus (śro., 17 Lut. 10, 15:33)
#6

A w czym nam może pomóc, zwykłym śmiertelnikom ten art?

S
slash (śro., 17 Lut. 10, 15:35)
#7

W operacji na bitach? Nie jestem pewien, strzelałem..

Easeful (śro., 17 Lut. 10, 17:29)
#8

praktycznie nie jest to potrzebne, ale art jest bardzo dobry i przybliżył mi działanie tych bitów :P 10/10

Platyna (śro., 17 Lut. 10, 17:40)
#9

Może w GMie faktycznie nie są zbyt potrzebne, ale w chociażby w C++ się przydają.

Może jeszcze jakiś przykład zastosowania... O wiem. Możemy w bardzo prosty sposób wygenerować wszystkie podzbiory jakiegoś zbioru. Tworzymy sobie inta w którym kolejne bity odpowiadają kolejnym elementom zbioru. Jeśli dany bit ma wartość 1 to element bierzemy, a jeśli 0 to nie. Zwiększając te liczbę o 1 generujemy kolejne podzbiory od pustego po wykorzystujący wszystkie elementy. To może być przydatne gdy każdemu możliwemu podzbiorowi chcemy przyporządkować jakąś komórkę tablicy na przykład. :)

S
slash (śro., 17 Lut. 10, 17:41)
#10

Każdy zrozumiał, o co chodzi :P ..

gnysek (śro., 17 Lut. 10, 18:22)
#11

No ale jak używasz np. 39dll, to art się bardzo przyda :) tak samo jak operujesz na plikach

p
pablo1517 (Wto., 23 Lut. 10, 06:48)
#12

Ja szczerze mówiąc dalej nie rozumiem jak można by to zastosować w 39dll xD

Dawidds (Wto., 23 Lut. 10, 07:39)
#13

pablo, jak masz do wysłania np. 2 zmienne 0-15 to zamiast je wysyłać osobno możesz je spokojnie upchnąć w jeden bat :)

gnysek (Wto., 23 Lut. 10, 16:26)
#14

Albo jak mam 8 zmiennych true/false :) Nawet jest taka funkcja buildbyte w 39dll :)

p
pablo1517 (Sob., 26 Mar. 11, 12:24)
#15

Ciekawostka, XOR pozwala na zamienienie zmiennych miejscami, tzn. Jeśli mamy a i b, i chcemy by a przybrała wartość b, i b przybrała wartość a, to z reguły ludzie tworzą sobie 3 dodatkową zmienną pomocniczą. XOR pozwala się bez niej obyć.
a=a^b;
b=a^b;
a=a^b;
I już zamienione :D

Najnowsze wersje GameMakera:

Stabilna
2024.8.1.171 • 2024.8.1.218
wydana 75 dni temu
LTS
2022.0.3.83 • 2022.0.3.98
wydana  3 dni temu
Beta
2024.1100.0.686 •
2024.1100.0.707
 0.13.0

wydana 9 dni temu
= IDE, = Runtime, = GMRT
Użytkownicy online
1 użytkownik aktywny:
gości: 1,
(~ostatnie 15 minut)
Discord
42 użytkownicy online na discordzie:
Kysiu, 🧁Cupcake🧁, Alice, Nitro Slav, Carl-bot, EchoDuck, p..., Jamabaiz (Matrix_), GibkiKaktus, Grela, Wielki Druid, m..., Kuzyn, GMRussell, OdrzuconyKrakers, fervi, 𝕳𝖚𝖌𝖔 𝕲𝖔𝖓𝖝𝖆𝖑𝖊𝖝, Kalor, PhysX ᴺⱽᴵᴰᴵᴬ, antek, HappyOrange, Arrekin, MagnusArias, LadyLush, yazaa, Domeen0, Dyno, 🆅🅸🆃🅾74🅼, szmalu, Miłosz, m..., bagno, Sporek, l..., Jayu, moeglich, s..., Add92, Krzysiek1250, Shockah, Cosplyfanka, PeekoHiko
Shoutbox
gnysek (11:46, 17.11.24)
Witamy, witamy!
baca (12:22, 16.11.24)
To już 25 lat.. Witam po paru latach nieobecności.
gnysek (11:05, 15.11.24)
Natomiast obecne forum istnieje od 2004, jak z iglu.cz na gmclan.org przeszliśmy i od tego czasu nie było resetów danych.
gnysek (12:35, 13.11.24)
Ogólnie GMCLAN istnieje 22 lata, ale na to trofeum nie zrobiłem (jeszcze xD)
Chell (20:41, 08.11.24)
wow, ta emotka w ogóle nie wygląda jak : O xD
Chell (20:40, 08.11.24)
tylko? :O 4tk ma 15
Borek (18:12, 07.11.24)
Właśnie dostałem powiadomienie z forum, że jestem na GMClanie 18 lat :D Ja pierdzielę...
S
Sutikku (08:43, 18.10.24)
TIL, gamemaker jest starszy ode mnie
gnysek (16:04, 15.10.24)
Za równo miesiąc, GameMaker kończy 25 lat.
Wojo (15:38, 05.09.24)
Ciekawe
Starsze wpisy znajdziesz w Archiwum.
Ankieta
Ile zarobiłeś do tej pory na grach stworzonych w GM?