Operátorok
A mütyűrködés csúcsa: a bitoperátorok
A számítógép segítségével rengetegféle művelet elvégezhető, sőt a valóság milliónyi aspektusa modellezhető le. Ennek ellenére bevallom őszintén a jelenlegi tudásommal fogalmam sincs, hogy az alábbi bitműveleteket milyen konkrét probléma megoldására lehetne alkalmazni. Az általam olvasott Java-oktatási anyagok legtöbbjében nem szerepelnek vagy csak az operátorok között, felsorolás szintjén. A legkiterjedtebb magyarázatot Angster Erzsébet Objektumorientált tervezés és programozás, JAVA, 1. kötet című munkájában olvastam (197. oldal), bár ő sem tért ki a bitoperálás lehetséges alkalmazásaira.
Az mindenesetre bizonyos, hogy talán az egyik leggyorsabban elvégezhető műveletfajtával van dolgunk, mert nem hiszem, hogy a számítógépben műveletileg bármi is gyorsabb lehet 1 bit átállításánál. (Persze az is számít, hogy mely hardveren kell ezt megtennünk, hiszen mondjuk egy hagyományos HDD válaszideje messze alulmarad a műveleti memória válaszidejétől, a processzor regisztereiről nem is beszélve.)
Először nézzük meg a bitműveletek szintaktikai szimbólumait (a témához kapcsolódó alapfogalmak az Igazságtáblák című fejezetben tanulmányozhatók):
-
~ - bitenkénti NOT,
-
& - bitenkénti AND,
-
| - bitenkénti OR,
-
^ - bitenkénti XOR,
-
<< - biteltolás balra,
-
>> - biteltolás jobbra.
Ezután nincs más dolgunk, mint a bitmütyűrködést leellenőrizni futtatható kódokon keresztül. Kiindulópontunk a decimális 8 lesz, amely binárisan fogalmazva 1000. Tehát:
8dec = 1000bin
Először ellenőrizzük le állításunkat az Integer.toBinaryString() függvény segítségével, amely int típusú decimális számot binárissá, majd String típussá alakít át:
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(szam));
}
}
Végeredmény:
1000
A luciferi tagadás bitművelete, csakis 1 operandust igényel:
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(~szam));
}
}
Végeredmény:
11111111111111111111111111110111
Mi is történt?
A bitenkénti tagadás művelete végrehajtásra került, azonban az int adattípus teljes tárolási tartományára, azaz mind a 32 bitre (emlékezzünk vissza erre az értékre a Numerikus egész című fejezetből). Ez mutatja azt, hogy int (és persze más) adattípus esetén is a rendszer az összes dedikált memóriaterületet valóban lefoglalja. Észrevehetjük a sor jobb végén az 1000 "tagadását", ami 0111.
A bitművelet már 2 operandust igényel, vizsgálatához először nézzük meg igazságtábláját:
Az AND logikai művelet kimenete csakis akkor ad 1 (true) értéket, ha mindkét bemenet szintén 1. Ha a művelet második operandusának például a decimális 1 számot választjuk...
8dec = 1000bin
1dec = 0001bin
...akkor láthatjuk, hogy a 2 szám logikai AND műveletű összefuttatásakor nem lesz sehol közös 1 bit, ezért a végeredmény 0 lesz. A könnyebb olvashatóság érdekében ezt a szam3 nevű változóba tettük (int szam3 = szam & szam2;):
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(szam));
int szam2 = 1;
System.out.println(Integer.toBinaryString(szam2));
int szam3 = szam & szam2;
System.out.println(szam3);
System.out.println(Integer.toBinaryString(szam3));
}
}
Végeredmény:
1000
1
0
0
De ha a művelet második operandusának például a decimális 8 számot választjuk...
8dec = 1000bin
8dec = 1000bin
...akkor láthatjuk, hogy a 2 szám logikai AND műveletű összefuttatásakor lesz 1 db közös 1 bit, ezért a végeredmény szintén decimális 8 lesz:
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(szam));
int szam2 = 8;
System.out.println(Integer.toBinaryString(szam2));
int szam3 = szam & szam2;
System.out.println(szam3);
System.out.println(Integer.toBinaryString(szam3));
}
}
Végeredmény:
1000
1000
8
1000
Azt is láthatjuk, hogy a Java-konzol a bináris kiírást egyszerűsíti, hiszen az üres 0 biteket nem írta ki, ezek az 1-től balra szereplő bitek összesen 32 bit terjedelemben.
Ez a bitművelet szintén 2 operandust igényel, vizsgálatához először nézzük meg igazságtábláját:
Az OR logikai művelet kimenete csakis akkor ad 1 (true) értéket, ha valamelyik bemeneten találunk 1 bitet. Ha a művelet második operandusának például a decimális 1 számot választjuk...
8dec = 1000bin
1dec = 0001bin
...akkor a 2 szám logikai OR műveletű összefuttatásakor a végeredmény 1001 lesz, amely decimális 9 számnak felel meg:
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(szam));
int szam2 = 1;
System.out.println(Integer.toBinaryString(szam2));
int szam3 = szam | szam2;
System.out.println(szam3);
System.out.println(Integer.toBinaryString(szam3));
}
}
Végeredmény:
1000
1
9
1001
Ez a bitművelet szintén 2 operandust igényel, vizsgálatához először nézzük meg igazságtábláját:
A XOR logikai művelet kimenete csakis akkor ad 1 (true) értéket, ha kizárólag az 1 bemeneten megjelenik 1 true bit. Ha a művelet második operandusának például a decimális 13 számot választjuk...
8dec = 1000bin
13dec = 1101bin
...akkor a 2 szám logikai XOR műveletű összefuttatásakor a végeredmény 101 lesz, amely decimális 5 számnak felel meg:
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(szam));
int szam2 = 1;
System.out.println(Integer.toBinaryString(szam2));
int szam3 = szam ^ szam2;
System.out.println(szam3);
System.out.println(Integer.toBinaryString(szam3));
}
}
Végeredmény:
1000
1101
5
101
A művelet alapjában véve 1 operandusú, de Javában kötelezően meg kell adnunk az eltolás mértékét is, például 1-es eltolás esetén: szam << 1. A művelet végrehajtásakor tehát azt várjuk, hogy például decimális 8 számból...
8dec = 1000bin
...10000 legyen, ami decimális 16-nak felel meg:
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(szam));
int szam2 = szam << 1;
System.out.println(szam2);
System.out.println(Integer.toBinaryString(szam2));
}
}
Végeredmény:
1000
16
10000
De például 2-es eltolás esetén az eredmények is eszerint fognak változni:
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(szam));
int szam2 = szam << 2;
System.out.println(szam2);
System.out.println(Integer.toBinaryString(szam2));
}
}
Végeredmény:
1000
32
100000
A művelet alapjában véve 1 operandusú, de kötelezően meg kell adnunk az eltolás mértékét is, például 1-es eltolás esetén: szam >> 1. A művelet végrehajtásakor tehát azt várjuk, hogy például decimális 8 számból...
8dec = 1000bin
...100 legyen, ami decimális 4-nek felel meg:
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(szam));
int szam2 = szam >> 1;
System.out.println(szam2);
System.out.println(Integer.toBinaryString(szam2));
}
}
Végeredmény:
1000
4
100
De például 2-es eltolás esetén az eredmények is eszerint fognak változni:
public class Main {
public static void main(String[] args) {
int szam = 8;
System.out.println(Integer.toBinaryString(szam));
int szam2 = szam >> 2;
System.out.println(szam2);
System.out.println(Integer.toBinaryString(szam2));
}
}
Végeredmény:
1000
2
10