A hardver szintjén a tranzisztorok csak be- vagy kikapcsolt állapotban lehetnek: ezt a két állapotot hagyományosan 1-gyel és 0-val jelöljük.
Ezek a binary digits, röviden bits.
A bits és a binary ábrázolás ismerete különösen fontos a rendszerprogramozóknak, akik alacsony szintű nyelveken dolgoznak.
A számítástechnika történetének nagy részében azonban a programozás fő célja az volt, hogy egyre kifinomultabb módokat találjon erre a bináris valóság elvonatkoztatására.
Pythonban (és sok más magas szintű programozási nyelvben) int, float, string és más definiált típusokkal dolgozunk, egészen a hang- és videoformátumokig.
Rábízzuk a Python belső működésére, hogy (végül) mindent bitekre fordítson.
Ennek ellenére a bitenkénti operátorok és a bitenkénti műveletek használata néha jelentős előnyt jelenthet a sebesség és a memóriahasználat terén, még egy olyan magas szintű nyelvben is, mint a Python.
Nem meglepő módon a Python tízes számrendszerbeli számokkal kommunikál a felhasználóval, de a programozó felülírhatja ezt az alapértelmezést.
Sőt, a Python készséggel elfogad int értékeket binary, hexadecimal vagy octal formátumban, és gond nélkül végez matematikai műveleteket köztük.
További részletekért nézd meg a binary-octal-hexadecimal fogalmat.
A bináris számokat 0b előtaggal írjuk le, ahogy a hexadecimális számokhoz a 0x használható (a hex számok tömör módját adják a 4 bites csoportok ábrázolásának), az oktális számokhoz pedig az oct.
Többféleképpen is átalakíthatunk egész számokat bináris stringgé; a változatok abban térnek el, hogy tartalmazzák-e a 0b előtagot, és hogy támogatják-e a bal oldali nullákkal való feltöltést:
# Binary entry.
>>> 0b10111
23
# Converting an int display to binary string, with prefix.
>>> bin(23)
'0b10111'
>>> number = 23
# Binary without prefix, padded to 8 digits.
>>> format(number, '08b')
'00010111'
# Same format, but using an f-string.
>>> f"{number} in decimal is {number:08b} in binary and {number:x} in hex"
'23 in decimal is 00010111 in binary and 17 in hex'
Bitwise LogicA bools fogalomban a logikai operátorokról volt szó: az and, or és not, amelyeket Boolean (True és False) értékekkel használunk.
Ugyanezek a logikai szabályok érvényesek, amikor bitekkel dolgozunk.
A logikai operátorok bitenkénti megfelelőit viszont, azaz a & (and), | (or), ~ (not) és ^ (XOR) operátort a bináris ábrázolás minden egyes bitjére alkalmazzuk, ahol az 1 a True („be"), a 0 pedig a False („ki") jelentést kapja.
Talán világosabb lesz egy példa a bitenkénti & operátorra:
>>> x = 0b01100110
>>> y = 0b00101010
>>> format(x & y, '08b')
'00100010'
Csak azok a pozíciók lesznek 1-esek az eredményben, amelyek mindkét bemeneti számban 1-et tartalmaznak.
A bitenkénti &-et gyakran arra használjuk, hogy egyetlen biteket emeljünk ki egy tömörített True/False értékhalmazból, például egy alkalmazás felhasználó által beállítható opciói közül.
Így az egyes bitek értéke vezérelheti a program logikáját:
>>> number = 0b0110
>>> number & 0b0001 > 0
False
>>> number & 0b0010 > 0
True
A bitenkénti | (or) esetén 1 kerül az eredménybe, ha a bemenetek valamelyikében 1 van:
>>> x = 0b01100110
>>> y = 0b00101010
>>> format(x | y, '08b')
'01101110'
A ^ operátor a bitenkénti exkluzív vagy (xor) műveleté: 1 kerül az eredménybe, ha a bemenetek valamelyikében szerepel, de nem mindkettőben.
Ez a szimbólum ismerős lehet a sets fogalomból, ahol a set szimmetrikus differenciáját jelöli, ami ugyanaz, mint a halmazokra alkalmazott xor.
Ha a xor ^ furcsának tűnik, jó tudni, hogy messze ez a kriptográfia leggyakoribb művelete.
>>> x = 0b01100110
>>> y = 0b00101010
>>> format(x ^ y, '08b')
'01001100'
Végül itt van a ~ operátor (a tilde karakter), ami a bitenkénti not: egyetlen bemenetet vesz, és az összes bitet megfordítja, ami talán nem az az eredmény, amire számítottál!
Az ábrázolásban minden 1 0-vá változik, és fordítva.
A részleteket lásd a következő szakaszban.
A tízes számrendszerbeli ábrázolásban a pozitív és negatív számokat úgy különböztetjük meg, hogy a számjegyek elé + vagy - jelet írunk.
Ezeknek a jeleknek a használata bináris szinten nem bizonyult hatékonynak a digitális számítástechnikában, ráadásul felvetette azt a problémát, hogy a +0 nem ugyanaz, mint a -0.
A - és a + helyett minden modern számítógép two's complement ábrázolást használ a negatív számokhoz, egészen a szilíciumlapka szintjéig.
Ez azt jelenti, hogy minden bit megfordul, és egy számot negatívként értelmezünk, ha a bal szélső bit (más néven a „legjelentősebb bit", rövidítve MSB) 1.
A pozitív számok MSB-je 0.
Ennek az ábrázolásnak az az előnye, hogy a nullának csak egy változata van, így a programozónak nem kell -0-val és +0-val foglalkoznia.
A negatív és pozitív számok ilyen ábrázolása bonyodalmat okoz a Pythonban: a nyelv magjában belül nincsenek olyan véges méretű egészek, mint az int32 vagy az int64.
A „modern" Pythonban az int értékek mérete korlátlan (csak a hardver kapacitása szab határt nekik), és egy negatív vagy bitenként megfordított szám bal oldalán (elméletileg) végtelen sok 1 áll, ahogy egy pozitív szám bal oldalán korlátlan sok 0.
Emiatt nehéz hasznos példát adni a bitenkénti bitwise not műveletre:
>>> x = 0b01100110
>>> format(x, '08b')
'01100110'
# This is a negative binary (not twos-complement display).
>>> format(~x, '08b')
'-1100111'
# Decimal representation.
>>> x
102
# Using the Bitwise not, with an unintuitive result.
>>> ~x
-103
Ez nem az a 0b10011001, amit a rögzített méretű egészeket használó nyelvekben látnánk.
A ~ operátor csak előjel nélküli byte- vagy egésztípusokkal, illetve rögzített méretű egésztípusokkal működik a várt módon.
Ezeket a numerikus típusokat olyan külső csomagok támogatják, mint a NumPy, a pandas és a sympy, a Python magja viszont nem.
A gyakorlatban a Python-programozók gyakran csak pozitív számokkal használják a &, |, ^ operátort és a lentebb leírt eltoló operátorokat.
A negatív számokkal végzett bitenkénti műveletek sokkal ritkábbak.
Az egyik technika, hogy egy negatív értékhez hozzáadjuk a 2**32 (or 1 << 32) értéket, hogy előjel nélküli int-t kapjunk, de ezt nehéz kézben tartani.
Egy másik stratégia a ctypes modul használata és C-stílusú egésztípusok alkalmazása, de ez ugyanolyan nehézkes.
Shift operatorsA balra toló operátor, x << y, az x összes bitjét y hellyel balra mozgatja, és az új hézagokat nullákkal tölti fel.
Vegyük észre, hogy ez számtanilag ugyanaz, mint a számot (2**y)-nal megszorozni.
A jobbra toló operátor, x >> y, ennek az ellenkezőjét teszi.
Ez számtanilag ugyanaz, mint az egész osztás: x // (2**y).
Tartsd észben az előző, negatív számokról szóló szakaszt, és azokat a buktatókat, amelyekkel a Pythonban való eltolásuk jár.
>>> x = 8
>>> format(x, '08b')
'00001000'
# A left bit shift.
>>> x << 2
32
>>> format(x << 2, '08b')
'00100000'
# A right bit shift.
>>> format(x >> 2, '08b')
'00000010'