Skocz do zawartości
l0nger

Operacje bitowe

Rekomendowane odpowiedzi

Operacje bitowe

W tym poradniku opisze co to sa operacje na bitach, jak stosujemy przesuniecia oraz dowiemy sie co to sa operacje logiczne (koniunkcja, alternatywa, negacja).

Pierwsze pytanie, ktore nam sie nasywa: co to jest operacja bitowa?

Operacja bitowa - jest to dzialanie na grupach bitow. Z powodu sposobu przedstawienia pamieci sens ma dla liczb calkowitych (w tym znakow). Dane sa przechowywane w pamieci w systemie dwojkowym. Przyklady wprowadzajace:

0010

1001

0110

1000

W pozniejszych przykladach bede wzorowal sie na 4 bitach, poniewaz w pamieci komputera bajty skladaja sie z 8. Natomiast w PAWN wszystkie nasze zmienne sa 4 lub 8 bajtowe, istnieje rowniez 16 jak i 32. Ilosc dostepnych bitow okresla definicja cellbits.

Dobra, zaczynamy przygode!

1) Przesuniecia

Krotka zasada dzialania: Zmienia kolejnosc bitow na miejsce sasiednie.

Istnieja 4 typy przesuniec (operator stosowania w nawiasie!):

1.1) W lewo (<<)

1.2) W prawo arytmetycznie (>>)

1.3) W lewo logicznie (<<<)

1.4) W prawo logicznie (>>>)

Przyklady zastosowania oraz opis co i jak dziala ponizej :).

1.1) Przesuniecie w lewo:


1 << 3

Bity liczby jeden (0001) przesuwa sie w lewo o trzy pozycje. W brakujace miejsca wpisywane sa zera, bity wykraczajace poza zakres sa tracone.
0001: poczatkowo 0010: po przesunieciu o 1 0100: po przesunieciu o 2 1000: po przesunieciu o 3
Zatem nasze dzialanie (tj. 1 << 3) wynosi 1000 co po przeliczeniu na system dziesietny da nam liczbe 8. Efekt: przesuniecie w lewo o N pozycji rownoznaczne jest z mnozeniem przez 2 do potegi N. 1.2) Przesuniecie w prawo arytmetycznie:

7 >> 1

Bity liczby siedem (0111) przesuwa sie w prawo o jedna pozycje. W brakujace miejsca sa dopisywane bity o wartosci najstarszego bitu pierwotnej liczby, bity wykraczajace poza zakres sa tracone.
0111: poczatkowo 0011: po przesunieciu o 1
Zatem nasze 7 >> 1 wyniesie 0011 w systemie dziesietny da nam liczbe 3. Brak sa uzupelniane bitem znaku.

15 >> 1

1111: poczatkowo 1111: po przesunieciu o 1
Efektem jest przesuniecie w prawo o N pozycji rownoznaczne jest z dzieleniem calkowitym 2 do potegi N 1.3) Przesuniecie w prawo logicznie:

15 >>> 1
Bity liczby pietnascie (1111) przesuwa sie w prawo o jedna pozycje. W brakujace miejsce dopisywanie sa zera, bity wykraczajace poza zakres sa tracone.
1111: poczatkowo 0111: po przesunieciu o 1
Zatem 15 >>> 1 wyniesie 0111, co po przeliczeniu na system dziesietny da nam liczbe 7. Braki uzupelniane sa zerami. 2) Operacje logiczne Dzialania logiczne wykonywane sa miedzy odpowiednimi bitami obu liczb. 2.1) Koniunkcja (inaczej iloczyn (operator &)):

8 & 2
Zapis dziesietny tych liczb na binarny:

1000 // 8

0010 // 2

Teraz bierzemy kolejne bity, wyznaczajac ich iloczyny. Rozpiska:

// tablica prawd

1 & 1 = 1

1 & 0 = 0

0 & 0 = 0

0 & 0 = 0

1000 0010 Wykonujemy dzialanie 1 & 0 = 0, zatem pierwszy bit od lewej strony jest rowny 0. 1000 0010 Wykonujemy dzialanie 0 & 0 = 0, zatem drugi bit od lewej strony jest rowny 0. 1000 0010 Wykonujemy dzialanie 0 & 1 = 0, zatem drugi bit od lewej strony jest rowny 0. 1000 0010 Wykonujemy dzialanie 0 & 0 = 0, zatem drugi bit od lewej strony jest rowny 0. Zatem 8 & 2 = 0000, po przeliczeniu na system dziesietny daje nam 0. 2.2) Alternatywa(inaczej suma(operator |)): Zrobimy tak jak w przykladzie koniunkcji, tylko, ze tutaj dodamy wartosci.

5 | 3
Zapis dziesietny na binarny

0101 // 5

0011 // 3

Teraz bierzemy kolejne bity, wyznaczajac ich sume. Rozpiska:

1 | 1 = 1

1 | 0 = 1

0 | 1 = 1

0 | 0 = 0

0101 0011 Wykonujemy dzialanie 0 | 0 = 0, zatem pierwszy bit od lewej strony jest rowny 0. 0101 0011 Wykonujemy dzialanie 1 | 0 = 1, zatem drugi bit od lewej strony jest rowny 1. 0101 0011 Wykonujemy dzialanie 0 | 1 = 1, zatem drugi bit od lewej strony jest rowny 1. 0101 0011 Wykonujemy dzialanie 1 | 1 = 1, zatem drugi bit od lewej strony jest rowny 1. Zatem 5 | 3 = 0111, po przeliczeniu na system dziesietny daje nam 7. 2.3) Roznica symetryczna XOR (operator ^)):

3 ^ 1
Zapis dziesietny na binarny

0011 // 3

0001 // 1

Teraz bierzemy kolejne bity, wyznaczajac ich roznice symetryczna. Rozpiska:

1 ^ 1 = 0

1 ^ 0 = 1

0 ^ 1 = 1

0 ^ 0 = 0

0011 0001 Wykonujemy dzialanie 0 ^ 0 = 0, zatem pierwszy bit od lewej strony jest rowny 0. 0011 0001 Wykonujemy dzialanie 0 ^ 0 = 0, zatem drugi bit od lewej strony jest rowny 0. 0011 0001 Wykonujemy dzialanie 1 ^ 0 = 1, zatem drugi bit od lewej strony jest rowny 1. 0011 0001 Wykonujemy dzialanie 1 ^ 1 = 0, zatem drugi bit od lewej strony jest rowny 0. Zatem 3 ^ 1 = 0010, po przeliczeniu na system dziesietny daje nam 2. 2.4) Negacja (operator ~)): Nie mylic z negacja warunkowa.

~1
Zamiana systemu dziesietnego na binarny

0001 // 1

Operacja jednoargumentowa - nie porownujemy bitow ze wzorcem, a jedynie zamieniamy wszystkie 0 na 1 i odwrotnie. Stad:
~0001

1110
Przyklad pokazuje idee - nie ma sensu przeliczac na dziesietny, poniewaz w PAWN nie spotkamy 4 bitowego typu integer. Gdy na liczbe w pamieci komputera spojrzymy na zbior wartosci logicznych (1 lub 0) to w prosty sposob wykorzystac mozemy operacje bitowe do przechowania lub sprawdzania czesci skladowej w calosci. Dokladniej chodzi o to, ze w kazdej wartosci 0 lub 1 jest przypisywana jakas funkcja (znaczenie), Mamy ciag:

0101

Mozemy to przeliczyc na system dziesietny, ale latwiej jest pracowac w takiej postaci. Kazda liczba to informacja czy dana funkcja skladowa jest aktywna (1) lub nie (0).

Oryginalna tresc by R3D
Edytowane przez AXV

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Fajna sprawa, mogłeś wspomnieć o zastosowaniu w pawn czyli przykładowo oprogramowaniu flag - możemy nimi zastąpić wartości typu bool. Co na tym zyskujemy? Pamięć. Przykładowo 8 bool'i zajmuje 8 bajtów a korzystając z flag zapiszesz te same dane na jednym bajcie. Może się to wydawać nie wiele, ale jeśli masz milion bool'i w kodzie to zaoszczędzisz kilka megabajtów.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Pisałem ten poradnik 2 lata temu, aktualnie nie mam styczności z tym językiem, także jeżeli ktoś się na tym zna i chce o tym napisać - proszę bardzo lub jeżeli nikt nie będzie chciał to sam napisze, ale na pewno nie teraz - w tym okresie.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Pisałem ten poradnik 2 lata temu, aktualnie nie mam styczności z tym językiem, także jeżeli ktoś się na tym zna i chce o tym napisać - proszę bardzo lub jeżeli nikt nie będzie chciał to sam napisze, ale na pewno nie teraz - w tym okresie.

Noo troszkę temat odkopałem, oczywiście nie zmuszam do niczego ;).

P.S. Wiedza o programowaniu w pawn Ci tak szybko nie wyleci z głowy :D

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Tutaj można się co nieco dowiedzieć o operacjach bitowych:

http://blog.heintze.pl/2011/09/20/php-operacje-na-bitach-w-praktyce/
Czyli: głównie używa się tego do uprawnień(czy może używać pojazdów grupowych, na forach czy może dział przeglądać, itd.)
W PAWN używa się tego podobnie.
 
EDIT:
http://forum.sa-mp.com/showthread.php?t=433067
A tutaj o zastosowaniu w PAWN. ;)
 
EDIT 2:
Oraz najważniejsze informacje - czyli te które najbardziej was pewnie interesują:

Purpose of Bit-wise Operators


(Example:Car Components Example showing how Key Presses in SAMP works)
Now that you have a basic idea, the purpose of bitwise operations can be explained clearly.The main purpose of bit-wise operators are for efficiency and space(even though space saved hardly matters.Why??n00b , they are bits not TB).And those are the only way a programmer can manipulate with bits.Bit-wise operators are unique compared to other operators.

The most common purpose is to group bools(making them flags).Storing a set of flags or bools in one variable.This is exactly the same whats done with Keys in SA:MP

This example is fun and interesting....messing with the cars components with bits
Suppose you have a Car, it has options such as Nitros,Sideskrits,etc and you need to store it in your script.
Instead of bool arrays (arrays need some computation before they can be used, computer must do some mathematics before it can fetch the data from the memory, example a[4][5] ,the computer must do 4 * (size of first array) + 5 to get the right data)

Instead you can do all of it in one variable and use a few instructions
By FLAGS = FLAG1 | FLAG2 | FLAG2;

You probably din't get it.After this example you will!

(The flags must be defined like this, bit-wise:binary:2 baseowers of 2)
Code:
#define SIDESKRITS 1
#define NITROS 2
#define TINT 4
#define BOMB 8
...8...16....32...64...128.....256....not 1,2,3,4,5,6,7
The reason is we want just one bit to be set in the whole integer.-_- whats that now??With the example you'll understand.

int carComponents; //The variable to store the flags
carComponents = SIDESKRITS | NITROS | TINT

And these would be *(numbers) in binary
1 - 00000001
2 - 00000010
4 - 00000100
8 - 00001000

This is why we choose powers of 2 so that we just get 1 bit set, now still why do we need just one bit on??the next example will tell! Examples make automatic explanations

According to my flags settings the car has (SIDESKRITS | NITROS | TINT )
This would give the value for carComponents as 111 in binary.We separate the flags by OR, so lets OR to get the value.

00000001
00000010
00000100
------------
00000111 - This is the value in carComponents after " carComponents = SIDESKRITS | NITROS | TINT "
By ORing the flags we set the variable.

Now did you get why we need powers of 2??
No??

And how do we check the flags??
Code:
if(carComponents & NITROS) { print("The car has nitros"); }
This is exactly exactly exactly exactly exactly the same way the SAMP works out the Key Presses, if you had used them before you might recognize the '&' over there and a similar format for checking the keys.

Remember??
Code:
if(newkeys & KEY_SOMETHING)
If you never tried it before CLICK CLICK

And how this works??
00000111 - this is what is the value of the carComponents after ORing with the flags
00000010 - Nitros is 2 (#define - defined above)
-----------
00000010 - After ANDing: It's a value(its 2) is one of which "if"(if..else) considered as true

So "if(carComponents & NITROS)" will end up as "if(2)" //The if block is executed
Don't know why it executes the if when if(2)??
Click Me

So the Nitros is set!!Yeah!We had set it "carComponents = SIDESKRITS | NITROS | TINT"

Lets test whether BOMB was set in the car
Code:
if(carComponents & BOMB) { print("Oh My God!!There's a BOMB!!!"); }
00000111 - this is what is set in my example
00001000 - BOMB is 8 (#define - defined above)
-----------
00000000 - Oh Yeah! 0 means false so if will not be executed and thus there is no BOMB installed in the car
So AND it with the flag.

Code:
carComponents = SIDESKRITS | NITROS | TINT

We had never set the bomb so the check operation is correct!!The car is safe so take a freaking ride on it!!

Edytowane przez Neproify

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Mam pytanie. Na forum samp-wiki znalazłem coś takiego:
 

enum Door
{
	DOOR_HOOD,
	DOOR_TRUNK,
	DOOR_DRIVER
	DOOR_PASSENGER
}
 
enum DoorState(<<= 1)
{
	IS_OPENED = 1,
	IS_DAMAGED,
	IS_REMOVED
}
 
stock GetDoorState(doorStates, Door:door, DoorState:doorState)
	return (doorStates >>> (8 * door)) & doorState;

Mam pytanie.
Co oznaczają trzy znaki większości? To ma coś wspólnego z operacjami bitowymi?

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Owszem, ma bo to też operacja na bitach.

new val= 0b11111111111111111111111111111111; // 32 bity
new shr= 1;

new val1= val >> shr;
new val2= val >>> shr;

printf("\n%08x\n%08x", val1,val2);
printf("\n%032b\n%032b", val1,val2);
Edytowane przez PrzMas

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Mamy taki kod:

new val= 0b11111111111111111111111111111111; // tak jak po '0x' piszemy wartości szesnastkowe tak samo po '0b' piszemy wartości binarne
new shr= 4;

new val1= val >> shr;
new val2= val >>> shr;

printf("\n%032b\n%032b >>\n%032b >>>", val,val1,val2);

Dla 'val' równego '0b11111111111111111111111111111111' zostanie wydrukowane:

11111111111111111111111111111111
11111111111111111111111111111111 >>
00001111111111111111111111111111 >>>

Dla 'val' równego '0b01111111111111111111111111111111' zostanie wydrukowane:

01111111111111111111111111111111
00000111111111111111111111111111 >>
00000111111111111111111111111111 >>>

Z prawej strony bity są obcinane.

Dla operatora >> jest powielany pierwszy bit od lewej strony, a dla operatora >>> z lewej strony zawsze są powielane zera.

Różnica między operatorami jest zauważalna, gdy dochodzi do przekroczenia zakresu integer podczas wykonywania operacji.

 

@DOWN

Możesz to wszystko sam posprawdzać print-ując odpowiednie wyniki w pętli o ustalonym kroku.

Znając sposób działania tych operatorów możesz też sobie rozpisać pewne przykłady.

Edytowane przez PrzMas

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Dzięki wielkie :) Bardzo mi pomogłeś.
Jeśli możesz i ci się chce, to odpowiedz jeszcze na dwa pytania, a będę spełniony :P

1. Tak przeliczając na system dziesiętny, to zrobienie x>>n spowoduje podzielenie x przez 2 do potegi n.
Co w takim razie w "naszym dziesiątkowym języku" zrobi x>>>n?

2. 

Różnica między operatorami jest zauważalna, gdy dochodzi do przekroczenia zakresu integer podczas wykonywania operacji.

2a. Czyli przy mniejszych liczbach nie ma różnicy?
2b. Jaka różnica występuje przy przekroczeniu zakresu integera? :P

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

  • Przeglądający   0 użytkowników

    Brak zarejestrowanych użytkowników przeglądających tę stronę.

×