Andrise programmeerimisalane WIKI
Bitioperatsioonid
Bitioperatsioonid on tegelikult tavalised numbritega opereerimised, ainsa erinevusega, et harjumuspärase kümnendsüsteemi asemel kasutatakse binaarseid numbreid. Kuna operatsioonide sisendid ja väljundid on reeglina siiski mitte binaar-, vaid kümnendkujul, võivad esmapilgul bitioperatsioonid paista üsna krüptilised ning ebaloogilised (12|5 = 13; 12^5 = 9 jne). Lahenduseks oleks operatsioonide ajaks unustada numbrite kümnendvormiline sisu ja vaadata kõike binaarselt. Sellisel juhul ebaloogilisus kaob ning võib selguda, et teatud juhtudel on bitioperatsioonid tegelikult üsnagi kasulikud.
Kui teistes keeltes on bitioperatsioonid võrreldes muude operatsioonidega reeglina kiiremad, siis JavaScriptis paraku jäävad need suhteliselt aeglaseks. Bitioperatsioone kasutatakse tegelikult väga harva (kui üldse) ning peamiselt tekitavad nad ainult segadust. Probleemiks on bitioperaatorite operaatorid & ning |, mis on väga sarnased loogiliste operaatoritega && ning ||. Kui programmeerija teeb trükivea, kasutades loogilise operaatori asemel bitioperaatorit, siis sellest ei järgne veateadet programmi kompileerimisel ega käivitusel. Tulemuseks võib seega olla väga raskesti avastatav viga.
32 bitised numbrid
Bitioperatsioonides teostatakse kõik operatsioonid 32 bitiste märgiga täisarvudega, kus vasakpoolseim bitt tähistab numbri märk (0: positiivne, 1: negatiivne). Kuna numbrid on JavaScriptis tegelikult topelttäpsusega 64 bitised reaalarvud, teisendatakse kasutatav number operatsiooni läbiviimiseks vastavalt 32 bitiseks. Vajadusel kustutates numbrist osad, mis lubatud 32 biti sisse ei mahu (näiteks numbri murdosa).
Iga operatsiooniga kaasnev teisendus (sisendid ja väljundid on 64 bitised numbrid, operatsiooniks kasutatakse aga 32 bitist numbrit) asetab programmi täitmisel masinale paratamatult mõningase täiendava koormuse. Mõningatel juhtudel - näiteks lippude haldamisel vabaneva mälu arvelt - võib selle lisanduva koormusega siiski leppida. Pealegi, väiksemas mastaabis on tegu suhteliselt ebaolulise kaoga.
Seega kuna numbreid käsitletakse binaarsüsteemis, tähendab see 32 kohalist jada mille liikmeteks võivad olla numbrid 0 ning 1. Näiteks numbrit 12 käsitletakse binaarsüsteemis kujul 1100 või õigemini 32 bitise numbrina
00000000000000000000000000001100
Kui me nüüd teostame selle numbriga mõne bitioperatsiooni, näiteks nihutame kõik bitid ühe sammu võrra paremale
12 >> 1
siis saame vastuseks numbri 6. Binaarkujul vaadates tähendab see
00000000000000000000000000000110
Ehk et nagu näha, ongi bitid liikunud ühe sammu võrra paremale. Kuna ruumi on täpselt 32 bitti, ei rohkem ega vähem, täidab operaator >> vasakule jäävad tühjad kohad märgibitiga (kõige vasakpoolsem bitt, mis näitab kas number on positiivne või negatiivne) ja kuna antud näites oli tegu positiivse arvuga, lisati vasakule null. Parempoolne üleliigne bitt kustutati operatsiooni käigus lihtsalt ära.
Võrdlusoperatsioonid
Bitioperatsioon AND - &
Operatsioon võrdleb kahe operandi bitte paaridena ning tagastab iga paari puhul väärtuse 1, kui mõlemad võrreldvad on seatud (1), vastasel korral tagastab väärtuse 0.
12 & 5
Vasak operand: 1100 // 12
Parem operand: 0101 // 5
----
Tulemus &: 0100 // 4
Bitioperatsioon OR - |
Operatsioon tagastab bitipaaride puhul väärtuse 1, kui vähemalt üks operand on seatud (1), vastasel korral tagastab väärtuse 0.
12 | 5
Vasak operand: 1100 // 12
Parem operand: 0101 // 5
----
Tulemus |: 1101 // 13
Bitioperatsioon XOR - ^
Operatsioon tagastab bitipaari puhul väärtuse 1, kui võrreldavad bitid on erinevad. Vastasel korral on tagastusväärtuseks 0.
12 ^ 5
Vasak operand: 1100 // 12
Parem operand: 0101 // 5
----
Tulemus ^: 1001 // 9
Bitioperatsioon NOT - ~
NOT ~ on unaarne operatsioon kasutades vaid ühte operandi. Tagastusväärtuseks on operandi peegelpilt - juhul kui bitt on seatud, on vastav tagastusbitt 0 ning vastupidisel juhul 1.
~12
Operand : 1100 // 12
----
Tulemus ~: 0011 // 3
Bittide nihked
Nihe vasakule - <<
Nihe vasakule liigutab kõik bitid operaatori parempoolse operandi väärtuse võrra vasakule. Bitid mis jäävad vasakult üle 32 biti piiri, kustutatakse. Paremale tekkivad tühjad kohad saavad väärtuseks nullid.
12 << 1
00000000000000000000000000001100 // 12 00000000000000000000000000011000 // 24
Nihe paremale - >>
Nihe paremale liigutab kõik bitid operaatori parempoolse operandi väärtuse võrra vasakule. Paremalt üle jäävad bitid kustutatakse, vasakul täidetakse tühjad kohad märgibitiga.
12 >> 1
00000000000000000000000000001100 // 12 00000000000000000000000000000110 // 6
Nulltäitega nihe paremale - >>>
Nulltäitega nihe paremale liigutab kõik bitid operaatori parempoolse operandi väärtuse võrra vasakule. Paremalt üle jäävad bitid kustutatakse, vasakul täidetakse tühjad kohad nulliga. Positiivse arvu korral on tulemus sama, mis tavalise nihkega paremale.
12 >>> 1
00000000000000000000000000001100 // 12 00000000000000000000000000000110 // 6
Lippude haldamine
Üheks heaks näiteks bitioperatsioonidel kasutamisel on liipude haldamine. Lipp on mingit olekut või sündmust tähistav muutuja, mida saab tähistada ühe bitiga. Kui bitil on väärtus 1, on lipp seatud. Ning vastupidi - kui bitil on väärtus 0, on lipp seadmata.
Bittide kujul lippe hoida on otstarbekas peamiselt kahel põhjusel. Esiteks saaks lippu hoida mälus näiteks ka numbrina, kasutades väärtuseid 0 ja 1, kuid sellisel juhul kuluks lipu staatuse hoidmiseks ühe asemel tervelt 64 bitti. Loomulikult pole vahet, kui kasutuses on ainult üks lipp - aga erinevus tuleb sisse lippude kombineerimisel ühe 32 bitise numbri sisse.
Teiseks kasulikuks omaduseks ongi just see lippude ühte kombineerimise võimalus. Sellisel juhul saab nende lippudega manipuleerida ühtse kogumina. Võimalik on kasutada operatsioonides bitimaske ning muuta nii korraga kõigi lippude olekuid ja positsioone numbri bitikohtades.
Erinevate bittide defineerimine
Lippude arvestamise lihtsustamiseks tasub kõigepealt defineerida erinevate bittide olekud.
var BITT_1 = 0x1; // 000001 var BITT_2 = 0x2; // 000010 var BITT_3 = 0x4; // 000100 var BITT_4 = 0x8; // 001000 var BITT_5 = 0x10; // 010000 var BITT_6 = 0x20; // 100000 var BITT_7 = 0x40; // ... var BITT_8 = 0x80; // ... var BITT_9 = 0x100; var BITT_10 = 0x200; var BITT_11 = 0x400; var BITT_12 = 0x800; var BITT_13 = 0x1000; var BITT_14 = 0x2000; var BITT_15 = 0x4000; var BITT_16 = 0x8000; var BITT_17 = 0x10000; var BITT_18 = 0x20000; var BITT_19 = 0x40000; var BITT_20 = 0x80000; var BITT_21 = 0x100000; var BITT_22 = 0x200000; var BITT_23 = 0x400000; var BITT_24 = 0x800000; var BITT_25 = 0x1000000; var BITT_26 = 0x2000000; var BITT_27 = 0x4000000; var BITT_28 = 0x8000000; var BITT_29 = 0x10000000; var BITT_30 = 0x20000000; var BITT_31 = 0x40000000; var BITT_32 = 0x80000000;
Lippude seadmine
Lippe saab seada bitioperaatoriga OR (|). Sellisel juhul jäävad puutumata kõik teised bitid v.a. bitt, mida tahetakse seada. Juhul kui bitt on juba olemas, jääb kõik samaks. Juhul kui aga pole, saab bitt väärtuse 1.
var lipud = 0; lipud |= BITT_1; //Seame lipu nr 1 lipud |= BITT_4; //Seame lipu nr 4 alert(lipud.toString(2)); // 1001
Korraga saab seada ka mitu bitti, kasutades selleks nn. bitimaski, mille saab samuti koostada kasutades bitioperaatorit OR.
var lipud = 0; var bitimask = BITT_1 | BITT_2; lipud |= bitimask;
Lippude väärtuste lugemine
Lipu väärtust on mugav lugeda bitioperaatoriga AND (&). Kui võrrelda numbrit ning kindlat bitti, siis juhul kui numbris on võrreldav bitt seatud, on vastuseks sama biti numbriline väärtus. Kui aga bitti pole, on vastuseks 0.
if(lipud & BITT_3){ alert('Bitt 3 on seatud'); }else{ alert('Bitt 3 on seadmata'); }
Mitme biti väärtuse kontrollimine bitimaskiga:
if(lipud & (BITT_1 | BITT_2 | BITT_3)){ alert('Bitid 1,2 ja 3 on seatud'); }else{ alert('Bitid ei ole seatud'); }
Lipu mahavõtmine
Lippu saab maha võtta (lippu tähistava biti bäärtuseks saab 0) liites lippudele operaatoriga AND bitioperatsiooni tehte NOT lipu väärtusest. Sellega muudame esiteks muutuja soovitud bitiga vastupidiseks, nii et kõik nullid muutuvad ühtedeks ning soovitud kohal asuv bitt muutub nulliks. Kui nüüd võrdleme seda lippudega kasutades operaatorit AND, jäävad kõik teised lipud muutmata (z & 1 = z), aga soovitud bitt muutub nulliks.
lipud &= ~BITT_2; // eemaldame lippude hulgast biti 2
Korraga mitme lipu väärtuse mahavõtmine bitimkaski kasutades:
lipud &= ~(BITT_1 | BITT_2 | BITT_3);
Lippude kasutamise näide
Käesolevas näites kasutame lippe kolme sahtliga kappi kujutava objekti sahtlite haldamiseks. Juhul kui lisame sahtlisse midagi, märgime et sahtel on kasutuses. Juhul kui teeme selle tühjaks, eemaldame lipu. Samuti saame uurida, kas sahtel on juba kasutuses või mitte.
var alumine_sahtel = 0x1; var keskmine_sahtel = 0x2; var ylemine_sahtel = 0x4; var kapp = { sahtlid: 0, sahtel_kasutusse: function(sahtel){ this.sahtlid |= sahtel; }, sahtel_tyhjaks: function(sahtel){ this.sahtlid &= ~sahtel; }, sahtli_info: function(sahtel){ if(this.sahtlid & sahtel){ alert('Sahtel on juba kasutuses!'); }else{ alert('Sahtel on tühi!'); }; } } kapp.sahtel_kasutusse(ylemine_sahtel | keskmine_sahtel | alumine_sahtel); kapp.sahtel_tyhjaks(alumine_sahtel | ylemine_sahtel); kapp.sahtli_info(alumine_sahtel);
Näites märgime esiteks kõik sahtlid täidetuks. Seejärel tühjendame alumise ning ülemise sahtli ja lõpuks kontrollime, mis seisus on alumine sahtel. Vastus on oodatult, et sahtel on tühi.
Käidud rada: • script_tag • nimeruumid • operaatorid • matemaatika • sissejuhatus • funktsioonid • andmetueuebid • xmlhttprequest • aeg_ja_kuupaev • bitioperatsioonid


