बाइनरी अंक अंततः आपके CPU या RAM के ट्रांजिस्टरों से सीधे जुड़े होते हैं। यह तय करते हैं कि हर ट्रांजिस्टर "चालू" है या "बंद"।
लो-लेवल हेरफेर, जिसे अनौपचारिक रूप से "बिट-ट्विडलिंग" कहा जाता है, सिस्टम भाषाओं में विशेष रूप से महत्वपूर्ण है।
हाई-लेवल भाषाएँ जैसे Julia आमतौर पर इस विवरण का अधिकांश हिस्सा छिपा देती हैं। हालाँकि, बेस भाषा में बिट-लेवल ऑपरेशनों की पूरी रेंज उपलब्ध है।
नोट: REPL में मानव-पठनीय बाइनरी आउटपुट देखने के लिए, नीचे दिए गए लगभग सभी उदाहरणों को bitstring() फंक्शन में लपेटना आवश्यक है। यह देखने में ध्यान भटकाने वाला है, इसलिए इस फंक्शन की अधिकांश उपस्थितियों को हटा दिया गया है।
पूर्णांक टाइप, चाहे साइन्ड हों या अनसाइन्ड, 1 और 0 की स्ट्रिंग के रूप में दर्शाए जा सकते हैं।
julia> bitstring(UInt8(5))
"00000101"
बिट-शिफ्ट बस सब कुछ एक निर्दिष्ट संख्या में स्थानों तक बाएँ या दाएँ खिसका देते हैं। UInt टाइप के साथ, कुछ बिट्स एक सिरे से गिर जाते हैं, और दूसरे सिरे को शून्यों से भर दिया जाता है:
julia> ux::UInt8 = 5
5
julia> bitstring(ux)
"00000101"
julia> ux << 2 # left by 2
"00010100"
julia> ux >> 1 # right by 1
"00000010"
प्रत्येक लेफ्ट-शिफ्ट वैल्यू को दोगुना कर देता है और प्रत्येक राइट-शिफ्ट उसे आधा कर देता है (ट्रंकेशन के अधीन)। यह दशमलव निरूपण में और स्पष्ट होता है:
julia> 3 << 2
12
julia> 24 >> 3
3
ऐसा बिट-शिफ्टिंग "उचित" अंकगणित की तुलना में बहुत तेज़ है, जिससे यह तकनीक लो-लेवल कोडिंग में बहुत लोकप्रिय हो गई है।
साइन्ड पूर्णांकों के साथ, हमें थोड़ा और सावधान रहने की आवश्यकता है।
लेफ्ट शिफ्ट अपेक्षाकृत सरल होते हैं:
julia> sx = Int8(5)
5
julia> sx # positive integer
"00000101"
julia> sx << 2
"00010100"
julia> -sx # negative integer
"11111011"
julia> -sx << 2
"11101100"
इस प्रकार धनात्मक साइन्ड पूर्णांकों को लेफ्ट-शिफ्ट करना अनसाइन्ड पूर्णांकों के समान ही है।
ऋणात्मक वैल्यू दो का पूरक रूप में संग्रहीत की जाती हैं, जिसका अर्थ है कि सबसे बायाँ बिट 1 होता है। लेफ्ट-शिफ्ट के लिए कोई समस्या नहीं, लेकिन राइट-शिफ्ट करते समय हम सबसे बाएँ बिट्स को कैसे भरते हैं?
julia> sx >> 2 # simple for positive values!
"00000001"
julia> -sx # negative integer
"11111011"
julia> -sx >> 2 # pad with repeated sign bit
"11111110"
julia> -sx >>> 2 # pad with 0
"00111110"
>> ऑपरेटर अंकगणितीय शिफ्ट करता है, जो साइन बिट को सुरक्षित रखता है।
>>> ऑपरेटर लॉजिकल शिफ्ट करता है, और शून्यों से भरता है जैसे कि संख्या अनसाइन्ड हो।
हमने एक पिछले कॉन्सेप्ट में देखा था कि ऑपरेटर && (और), || (या) और ! (नहीं) बूलियन वैल्यू के साथ उपयोग किए जाते हैं।
दो पूर्णांकों में बिट्स की तुलना करने के लिए समतुल्य ऑपरेटर & (बिटवाइज़ और), | (बिटवाइज़ या) और ~ (एक टिल्ड, बिटवाइज़ नहीं) हैं।
julia> 0b1011 & 0b0010 # bit is 1 in both numbers
"00000010"
julia> 0b1011 | 0b0010 # bit is 1 in at least one number
"00001011"
julia> ~0b1011 # flip all bits
"11110100"
julia> xor(0b1011, 0b0010) # bit is 1 in exactly one number, not both
"00001001"
यहाँ, xor() एक्सक्लूसिव-ऑर है, जिसे फंक्शन के रूप में उपयोग किया जाता है।
आपके एक दोस्त ने अभी-अभी आपको एक ज़रूरी राज़ वाला संदेश भेजा है। दूसरों के लिए उसे पढ़ना आसान न हो, इसलिए संदेश को बिट्स पर कई बदलाव करके एन्क्रिप्ट किया गया था। संदेश को डिक्रिप्ट करने में मदद के लिए आपको कुछ फंक्शन लिखने होंगे।
संदेश को डिक्रिप्ट करने का पहला कदम यह है कि एन्क्रिप्शन के दौरान हुई शिफ्टिंग को उलट दिया जाए और बिट्स को दाईं ओर वापस शिफ्ट कर दिया जाए। डिक्रिप्शन की आगे की प्रक्रियाओं में यह माना गया है कि बाईं ओर से 0 डाले जाते हैं।
shift_back() फंक्शन बनाइए जो एक वैल्यू और शिफ्ट करने के लिए जगहों की संख्या लेता है और शिफ्ट करता है।
shift_back(0b1001, 2) # => 0b0010
इसके बाद, कुछ बिट्स ऐसे हैं जिन्हें 1 पर सेट करना होता है।
set_bits() फंक्शन बनाइए जो एक वैल्यू और एक मास्क लेता है और वैल्यू के बिट्स को 1 पर सेट करने का परिणाम लौटाता है।
value का कोई बिट वहाँ 1 पर सेट होना चाहिए जहाँ mask का बिट भी 1 हो।
बाकी सभी बिट्स जैसे हैं वैसे ही रहने चाहिए।
set_bits(0b0110, 0b0101) # => 0b0111
एन्क्रिप्शन के दौरान कुछ बिट्स पलट दिए जाते हैं। संदेश को डिक्रिप्ट करने के लिए इन्हें वापस पलटना होगा।
flip_bits() फंक्शन बनाइए जो एक वैल्यू और मास्क लेता है।
मास्क बताता है कि वैल्यू के कौन-से बिट्स पलटने हैं।
अगर mask में कोई बिट 1 है, तो value में वह बिट पलट दिया जाता है।
बाकी सभी बिट्स जैसे हैं वैसे ही रहते हैं।
flip_bits(0b1100, 0b0101) # => 0b1001
अंत में, कुछ ऐसे बिट्स भी हैं जो हमेशा 0 पर ही डिक्रिप्ट होते हैं।
clear_bits() फंक्शन बनाइए जो एक वैल्यू और एक मास्क लेता है।
जहाँ मास्क का बिट 1 है, वहाँ value के बिट्स 0 पर सेट होने चाहिए।
बाकी सभी बिट्स जैसे हैं वैसे ही रहने चाहिए।
clear_bits(0b0110, 0b0101) # => 0b0010
Exercism पर साइन अप कीजिए और Julia को 35 कॉन्सेप्ट128 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।