हार्डवेयर के स्तर पर ट्रांजिस्टर या तो चालू हो सकते हैं या बंद: ये दो अवस्थाएँ हैं, जिन्हें हम परंपरागत रूप से 1 और 0 से दर्शाते हैं।
ये ही binary digits हैं, जिन्हें छोटा करके bits कहा जाता है।
लो-लेवल भाषाओं में काम करने वाले सिस्टम प्रोग्रामर के लिए bits और binary की समझ खास तौर पर ज़रूरी है।
लेकिन कंप्यूटिंग के इतिहास का ज़्यादातर हिस्सा ऐसा रहा है कि प्रोग्रामिंग में प्राथमिकता इस बात को मिली है कि इस बाइनरी वास्तविकता को छिपाने के लिए लगातार और परिष्कृत तरीके खोजे जाएँ।
Python में (और कई अन्य हाई-लेवल प्रोग्रामिंग भाषाओं में) हम int, float, string और अन्य परिभाषित टाइपों के साथ काम करते हैं, यहाँ तक कि ऑडियो और वीडियो फॉर्मैट तक।
हम यह काम Python के अंदरूनी हिस्से पर छोड़ देते हैं कि वह (अंततः) हर चीज़ को बिट्स में बदल दे।
फिर भी, बिटवाइज़ ऑपरेटरों और बिटवाइज़ ऑपरेशनों का उपयोग कभी-कभी गति और मेमोरी की दक्षता में बड़ा फ़ायदा दे सकता है, Python जैसी हाई-लेवल भाषा में भी।
स्वाभाविक ही है कि Python उपयोगकर्ता से दशमलव संख्याओं के ज़रिए बातचीत करता है, लेकिन प्रोग्रामर इस डिफ़ॉल्ट को बदल सकता है।
वास्तव में, Python binary, hexadecimal या octal प्रारूप में दिया गया int सहज ही स्वीकार कर लेता है और इनके बीच गणितीय संक्रियाएँ भी बड़े आराम से कर देता है।
ज़्यादा जानकारी के लिए आप binary-octal-hexadecimal कॉन्सेप्ट देख सकते हैं।
बाइनरी संख्याएँ 0b उपसर्ग के साथ दर्ज की जाती हैं, ठीक वैसे ही जैसे hexadecimal के लिए 0x इस्तेमाल किया जा सकता है (हेक्स संख्याएँ 4 बिट्स के समूह दर्शाने का संक्षिप्त तरीका हैं), और octal संख्याओं के लिए oct।
पूर्णांकों को बाइनरी स्ट्रिंग में बदलने के कई तरीके हैं, जो इस बात में अलग-अलग हैं कि उनमें 0b उपसर्ग शामिल है या नहीं और वे बाईं ओर शून्य जोड़कर पूरा करना समर्थित करते हैं या नहीं:
# 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 Logicbools कॉन्सेप्ट में हमने बूलियन (True और False) वैल्यूओं के साथ इस्तेमाल होने वाले लॉजिकल ऑपरेटरों and, or और not पर चर्चा की थी।
बिट्स के साथ काम करते समय भी तर्क के वही नियम लागू होते हैं।
लेकिन लॉजिकल ऑपरेटरों & (and), | (or), ~ (not) और ^ (XOR) के बिटवाइज़ रूपों को बाइनरी निरूपण के हर बिट पर लागू किया जाता है, जिसमें 1 को True ("चालू") और 0 को False ("बंद") माना जाता है।
बिटवाइज़ & का एक उदाहरण इसे और साफ़ कर सकता है:
>>> x = 0b01100110
>>> y = 0b00101010
>>> format(x & y, '08b')
'00100010'
आउटपुट में सिर्फ़ वही स्थान 1 हो जाते हैं जहाँ दोनों इनपुट संख्याओं में 1 हो।
बिटवाइज़ & का इस्तेमाल अक्सर True/False वैल्यूओं के किसी सघन समूह में अलग-अलग बिट्स को अलग करने के लिए किया जाता है, जैसे किसी ऐप की उपयोगकर्ता द्वारा बदली जा सकने वाली सेटिंग्स।
इससे अलग-अलग बिट्स की वैल्यू प्रोग्राम के लॉजिक को नियंत्रित कर सकती है:
>>> number = 0b0110
>>> number & 0b0001 > 0
False
>>> number & 0b0010 > 0
True
बिटवाइज़ | (or) में, अगर इनपुट में से किसी एक में 1 है तो आउटपुट में 1 हो जाता है:
>>> x = 0b01100110
>>> y = 0b00101010
>>> format(x | y, '08b')
'01101110'
बिटवाइज़ exclusive or (xor) के ^ ऑपरेटर में, 1 तब होता है जब वह इनपुट में से किसी एक में आता है, लेकिन दोनों में नहीं।
यह चिह्न आपको sets कॉन्सेप्ट से जाना-पहचाना लग सकता है, जहाँ इसका इस्तेमाल set के सममितीय अंतर के लिए होता है, जो सेटों पर लागू xor के समान ही है।
अगर xor ^ अजीब लगे, तो जान लीजिए कि यह क्रिप्टोग्राफ़ी में अब तक की सबसे आम संक्रिया है।
>>> x = 0b01100110
>>> y = 0b00101010
>>> format(x ^ y, '08b')
'01001100'
अंत में, ~ ऑपरेटर है (टिल्ड अक्षर), जो एक बिटवाइज़ not है, जो एक ही इनपुट लेता है और सारे बिट्स को उलट देता है, जो शायद वह नतीजा न हो जिसकी आपको उम्मीद थी!
निरूपण का हर 1 बदलकर 0 हो जाता है, और इसका उलटा भी।
विवरण के लिए नीचे का खंड देखिए।
दशमलव निरूपण में हम धनात्मक और ऋणात्मक संख्याओं के बीच अंतर अंकों के बाईं ओर + या - चिह्न लगाकर करते हैं।
बाइनरी स्तर पर इन चिह्नों का इस्तेमाल डिजिटल कंप्यूटिंग के लिए अक्षम साबित हुआ और इससे यह समस्या खड़ी हुई कि +0 और -0 एक समान नहीं हैं।
- और + के बजाय, सभी आधुनिक कंप्यूटर ऋणात्मक संख्याओं के लिए two's complement निरूपण इस्तेमाल करते हैं, सिलिकॉन चिप के स्तर तक।
इसका मतलब है कि सारे बिट्स उलट दिए जाते हैं, और अगर सबसे बाईं ओर का बिट (जिसे "सबसे महत्वपूर्ण बिट" या MSB भी कहा जाता है) 1 हो, तो संख्या ऋणात्मक मानी जाती है।
धनात्मक संख्याओं का MSB 0 होता है।
इस निरूपण का फ़ायदा यह है कि शून्य का सिर्फ़ एक ही रूप होता है, इसलिए प्रोग्रामर को -0 और +0 संभालने की ज़रूरत नहीं पड़ती।
ऋणात्मक और धनात्मक संख्याओं को इस तरह दर्शाना Python के लिए एक उलझन जोड़ देता है: मुख्य भाषा के अंदर int32 या int64 जैसी सीमित पूर्णांक वाली अवधारणाएँ नहीं होतीं।
'आधुनिक' Python में int का आकार असीमित होता है (सीमा सिर्फ़ हार्डवेयर की क्षमता तय करती है), और ऋणात्मक या बिट-उलटी संख्या के बाईं ओर (सैद्धांतिक रूप से) अनंत 1 होते हैं, ठीक वैसे ही जैसे धनात्मक संख्या के बाईं ओर असीमित 0 होते हैं।
इस वजह से bitwise not का कोई उपयोगी उदाहरण देना मुश्किल हो जाता है:
>>> 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
यह वह 0b10011001 नहीं है जो हमें स्थिर आकार वाले पूर्णांकों वाली भाषाओं में दिखता है।
~ ऑपरेटर अपेक्षा के अनुसार सिर्फ़ अनसाइन्ड बाइट या पूर्णांक टाइपों के साथ, या स्थिर आकार वाले पूर्णांक टाइपों के साथ ही काम करता है।
ये संख्यात्मक टाइप तीसरे पक्ष के पैकेजों जैसे NumPy, pandas और sympy में उपलब्ध हैं, लेकिन मुख्य Python में नहीं।
व्यवहार में, Python प्रोग्रामर अक्सर &, |, ^ और नीचे बताए गए शिफ्ट ऑपरेटरों का इस्तेमाल सिर्फ़ धनात्मक संख्याओं के साथ करते हैं।
ऋणात्मक संख्याओं के साथ बिटवाइज़ ऑपरेशन बहुत कम आम हैं।
एक तरीका यह है कि किसी ऋणात्मक वैल्यू में 2**32 (or 1 << 32) जोड़कर int को अनसाइन्ड बना दिया जाए, लेकिन इसे संभालना मुश्किल हो जाता है।
एक और तरीका ctypes मॉड्यूल के साथ काम करना और c-शैली के पूर्णांक टाइप इस्तेमाल करना है, लेकिन यह भी उतना ही बोझिल है।
Shift operatorsबाएँ शिफ्ट ऑपरेटर x << y, x के सारे बिट्स को y स्थान बाईं ओर खिसका देता है और नई खाली जगहें शून्य से भर देता है।
ध्यान दीजिए कि गणित के हिसाब से यह किसी संख्या को (2**y) से गुणा करने के बराबर है।
दाएँ शिफ्ट ऑपरेटर x >> y उलटा काम करता है।
यह गणित के हिसाब से पूर्णांक विभाजन x // (2**y) के बराबर है।
Python में ऋणात्मक संख्याओं को शिफ्ट करते समय पिछले खंड की बातें और उनकी आम गलतियाँ ध्यान में रखिए।
>>> 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'