বিট-ওয়াইজ অপারেটর (Bitwise Operator)

Jul 27, 2013

বিটওয়াইজ অপারেটর এবং বিটওয়াইজ অপারেশনগুলো প্রোগ্রামিং এর ক্ষেত্রে অনেক গুরুত্বপূর্ণ ভূমিকা পালন করতে পারে যার মূল কারণ হল এই অপারেটরের কাজগুলো কম্পিউটার অত্যন্ত দ্রুত সম্পন্ন করতে পারে। যোগ/বিয়োগ/গুণ/ভাগ ইত্যাদির চেয়ে একটা বিটের মান পরিবর্তন করে দেওয়া কম্পিউটারের জন্য অনেক সহজ কাজ। তাই অনেকক্ষেত্রেই বিট-ওয়াইজ অপারেটর ব্যবহারের মাধ্যমে হিসাব সম্পন্ন করলে অনেক সময় বাঁচানো যায়, তথা প্রোগ্রামের এফিশিয়েন্সি অনেকাংশে বৃদ্ধি করা যায়।

কম্পিউটার যেকোন তথ্যকে নাম্বার হিসেবে মেমরিতে রাখে। আর এই নাম্বারগুলো থাকে দ্বিমিক আকারে, অর্থাৎ ২ ভিত্তিক নাম্বার বা বাইনারি নাম্বার হিসেবে। বাইনারিতে কোন নাম্বারকে প্রকাশ করার জন্য কেবল দুটি ডিজিট রয়েছে, 0 এবং 1 । প্রতিটি 0 বা 1 হল একেকটি বিট। বিট-ওয়াইজ অপারেটরগুলো সমগ্র নাম্বার নিয়ে কাজ না করে এই বিটগুলোর একেকটির উপর পৃথক পৃথকভাবে কাজ করে। যেহেতু একটি বিটে কেবল 0 এবং 1 এই দুটি ডিজিটের একটি থাকতে পারে, তাই যেকোন বিট-ওয়াইজ অপারেটর দিয়ে যাই করা হোক না কেন, শেষ পর্যন্ত একটি বিট হয় পরিবর্তিত হয়ে 0/1 হবে নতুবা যা ছিল তাই থাকবে।

সি ল্যাঙ্গুয়েজে ৬টি বিট-ওয়াইজ অপারেটর রয়েছে, যথা -

১) & (AND)        ৪) ^ (XOR)
২) | (OR)         ৫) << (LEFT SHIFT)
৩) ~ (NOT)        ৬) >> (RIGHT SHIFT)

AND অপারেটরঃ

বিট-ওয়াইজ AND অপারেটরটি লজিকাল AND (&&) এর মতই কাজ করে। পার্থক্য হল, এটি প্রতিটি বিটের উপর আলাদা আলাদাভাবে কাজ করে। আমরা জানি কম্পিউটারে সত্য এবং মিথ্যা এই দুটি তথ্য যথাক্রমে 1 এবং 0 হিসেবে থাকে। 1 মানে সত্য, 0 মানে মিথ্যা। একইভাবে যখন কোন বিটে 1 থাকে সেই বিটটি সত্য বোঝায় এবং 0 থাকলে সেটি মিথ্যা বোঝায়। যখন দুটি নাম্বারের মধ্যে বিট-ওয়াইজ AND অপারেশন করা হয় তখন নাম্বার দুটির বিটগুলোর প্রতিটির মধ্যে AND অপারেশন হয় এবং সেখান থেকে নতুন একটি নাম্বার পাওয়া যায়। ঘটনাটি বোঝার জন্য একটি উদাহরণ দেখা যাক।

ধরা যাক আমাদের কাছে দুটি unsigned int আছে। হিসাবের সুবিধার জন্য আমরা unsigned int এর সাইজ ৩২ বিটের জায়গায় ধরে নিচ্ছি ৮ বিট। এখন ধরা যাক একটি নাম্বার 78 এবং আরেকটি নাম্বার 45। আমরা যদি নাম্বার দুটিকে বাইনারিতে রূপান্তর করি তাহলে তাদের বিট প্যাটার্ন পেয়ে যাব।

78 = 01001110
45 = 00101101

এখন যদি আমরা এদের মধ্যে বিট-ওয়াইজ AND অপারেশন করি এবং সেটিকে x নামক একটি ভেরিয়েবলে রাখি তাহলে তার সিন্ট্যাক্স হবে এরকম,

x = 78 & 45;

এখন যদি আমরা x এর ভেলু আউটপুট দেই, তাহলে পাব 12 । কিভাবে হল সেটি এদের বিট প্যাটার্ন লক্ষ্য করলেই বোঝা যাবে।

78 = 0100 1110
45 = 0010 1101
 & -----------
 x = 0000 1100 = 12

আমরা যদি একেবারে ডানের বিট থেকে দেখা শুরু করি এবং সেটিকে ১ম বিট বলি, তাহলে ব্যাপারটি এরকম দাঁড়ায়, ১ম বিটে আছে 78 এ 0 এবং 45 এ 1 । 0 এবং 1 এর মধ্যে AND করলে ফলাফল আসবে 0 । কারণ আমরা জানি, AND অপারেটর সত্য বা 1 রিটার্ন করে যখন এটি যাদের উপর কাজ করছে তারা উভয়ই সত্য হয়, অন্যথায় মিথ্যা বা 0 রিটার্ন করবে। এভাবে ২য় বিটে 1 এবং 0 AND হয়ে 0 হল। ৩য় বিটে 1 এবং 1 আছে। তাই এদের মধ্যে AND হয়ে 1 আসবে। একইভাবে বাকি ৫টি বিটেও একই ঘটনা ঘটছে। এভাবে করে প্রতিটি বিট আলাদা আলাদাভাবে AND অপারেশনের অন্তর্ভুক্ত হচ্ছে এবং সেই অনুযায়ী একটি বিট রিটার্ন করে সবশেষে নতুন একটি সংখ্যা তৈরি করছে।

OR অপারেটরঃ

বিট-ওয়াইজ AND অপারেটর কাজ বুঝতে পারলে বিট-ওয়াইজ OR অপারেটরের কাজ এমনিতেই বুঝতে পারা উচিৎ। মূল কাজটি আবারও একইভাবে হবে, প্রতিটি বিট অনুযায়ী। প্রতিটি বিটের উপর আলাদা আলাদাভাবে OR অপারেশন সংঘটিত হবে এবং সেখান থেকে একটি 0 বা 1 রিটার্ন হবে। AND অপারেটরের ক্ষেত্রে দুটি বিটই যদি 1 হয়ে কেবন তখন 1 রিটার্ন করে, অন্যথায় 0 রিটার্ন করে। সেখানে OR অপারেটরের ক্ষেত্রে দুটি বিটের যেকোন একটি সত্য বা 1 হলেই 1 রিটার্ন করবে। 0 রিটার্ন করবে শুধুমাত্র তখন যখন দুটি বিটই 0 হবে।

এখন যদি আমরা আবারও আগের দুটি নাম্বার বিবেচনা করি, 78 এবং 45, তাহলে দেখা যাক এদের মধ্যে OR অপারেশন করা হলে x এর মান কি হবে।

x = 78 | 45;

78 = 0100 1110
45 = 0010 1101
 | -----------
 x = 0110 1111 = 111

এবার আমরা x এর মান আউটপুট দিলে পাব 111 । বিট ধরে ধরে পর্যালোচনা করলে দেখা যাবে, ১ম বিটে আছে 0 এবং 1 যাদের মধ্যে OR করা হলে পাওয়া যাবে 1 । ২য় বিটে আছে 1 এবং 0 যাদের থেকে আবারও 1 পাওয়া যাবে। ৩য় বিটে আছে 1 এবং 1 যাদের থেকেও 1 পাওয়া যাবে। এভাবে করে ৫ম বিটে গেলে দেখা যাচ্ছে সেখানে আছে 0 এবং 0 যাদের মধ্যে OR করা হলে 0 রিটার্ন করবে। এভাবে সমগ্র নাম্বারের প্রতিটি বিট OR অপারেশনের মধ্যে দিয়ে যাবে এবং শেষ পর্যন্ত 111 এর বিট প্যাটার্ন পাওয়া যাবে।

NOT অপারেটরঃ

NOT অপারেটর কাজ অত্যন্ত সাধারণ। এটি একটি ইউনারি অপারেটর, অর্থাৎ এটির অপার‍্যান্ড একটি বা এটি একটি নাম্বারের উপর অপারেশন সংঘটিত করে। এর কাজ হল এটি যে নাম্বারের উপর অপারেশন চালায় সেই নাম্বারের প্রতিটি বিটকে ফ্লিপ করে দেয়। অর্থাৎ যে বিটে 0 আছে তাকে 1 বানায়, আর যে বিটে 1 আছে তাকে 0 বানিয়ে দেয়। আবারও ধরা যাক দুটি নাম্বার 78 এবং 45। এদেরকে বিট-ওয়াইজ NOT করা হলে কি হয় দেখা যাক।

 x = ~78;
78 = 0100 1110
 x = 1011 0001 = 177

 x = ~45;
45 = 0010 1101
 x = 1101 0010 = 210

এখানে আসলে আর বলার কিছু নেই। খুবই সহজ একটা ব্যাপার। যেখানে 0 আছে সেখানে 1 হবে আর যেখানে 1 আছে সেখানে 0 হবে।

XOR অপারেটরঃ

বিট-ওয়াইজ অপারেটরগুলোর মধ্যে একটি বিশেষ অপারেটর হল ^ (XOR) অপারেটর যাকে বলা হয় এক্সক্লুসিভ OR অপারেটর। এটি OR অপারেটরের মতই কাজ করে। তবে একটু বিশেষত্ব রয়েছে এতে। সেকারণেই নামের আগে ‘এক্সক্লুসিভ’ শব্দটা আছে! OR অপারেশনে যদি দুটি বিটের যেকোন একটি 1 হয় তাহলে 1 রিটার্ন করে। অতএব এটি 0 রিটার্ন করে কেবলমাত্র তখন যখন দুটি বিটই 0 হয়। সেখানে XOR অপারেটর 1 বা সত্য রিটার্ন করে শুধু মাত্র তখন যখন যেকোন একটি বিট, শুধুমাত্র একটি বিট 1 হয়। অর্থাৎ OR এ যেমন দুটি বিট 1 হলেও 1 রিটার্ন করত, এখন তেমনটি হবে না। XOR 1 রিটার্ন করে যখন দুটি বিটের একটি 1 এবং অপরটি 0 হয়। যদি দুটি বিটই 1 বা দুটি বিটই 0 হয় তাহলে 0 রিটার্ন করবে।

আবারও আমরা দুটি নাম্বার বিবেচনা করি, 78 এবং 45 ।

x = 78 ^ 45;

78 = 0100 1110
45 = 0010 1101
 ^ -----------
 x = 0110 0011 = 99

১ম বিটে আছে 0 এবং 1 যাদের XOR করলে 1 পাওয়া যাবে। ২য় বিটেও একই। ৩য় বিটে দুটি বিটই 1 হওয়ায় XOR এর নিয়ম অনুযায়ী এদের থেকে 0 রিটার্ন হবে। ৪র্থ বিটেও একইভাবে 0 হবে। ৫ম বিটের দুটি বিটই 0 থাকায় সেটিও 0 হবে। এভাবে করে আগাতে থাকলে সমগ্র বিট প্যাটার্ন পাওয়া যাবে যেটি হবে 99 এর বিট প্যাটার্ন। ফলে x এর মান আউটপুট দিলে আমরা 99 পাব।

LEFT SHIFT এবং RIGHT SHIFT অপারেটরঃ

SHIFT অপারেটরগুলো কোন নাম্বারের সমগ্র বিট প্যাটার্নকে ডানে বা বামে শিফট করে দেয়। অর্থাৎ যতঘর বলা হবে এরা নাম্বারের বিটগুলোর প্রতিটিকে স্ব স্ব অবস্থান থেকে ততঘর ডানে বা বামে সরিয়ে দিবে। LEFT SHIFT বামে এবং RIGHT SHIFT অপারেটর ডানে সরাবে। SHIFT অপারেটরের সিন্ট্যাক্সে অপারেটরের বামে থাকে যে নাম্বারটিকে শিফট করা হবে সেটি এবং ডানে থাকে যতঘর শিফট করা হবে সেই নাম্বার। ধরা যাক আমরা 78 কে 1 ঘর বামে শিফট করতে চাই। তাহলে ব্যাপারটি কিরকম হবে দেখা যাক।

 x = 78 << 1;

78 = 0100 1110
 x = 1001 1100 = 156 // একঘর বামে শিফট করার পর

লক্ষ্য করলেই বোঝা যাবে, যেটি এখানে হয়েছে তা হল, প্রতিটি বিট একঘর করে বামে সরে গিয়েছে। যখন LEFT SHIFT অপারেটরের মাধ্যমে বিটগুলোকে বামে সরানো হয় তখন একেবারে ডানে যে ফাঁকা বিট থেকে যায় সেটিতে 0 বসে যায়। আর একেবারে বামের যে বিটগুলো থাকে যেগুলোকে শিফট করার পর আর জায়গা থাকে না বসানোর সেগুলো হারিয়ে যায়। যেমন এক্ষেত্রে একেবারে বামের 0 হারিয়ে গিয়েছে। যদি আমরা 78 কে ২ ঘর বামে শিফট করতাম তাহলে ব্যাপারটি এমন হত,

 x = 78 << 2;

78 = 0100 1110
 x = 0011 1000 = 56 // দুইঘর বামে শিফট করার পর

এখানে পরিষ্কার বোঝা যাচ্ছে ২ ঘর বামে শিফট করার ফলে বামের দুটি বিট হারিয়ে গিয়েছে, যার মধ্যে একটি বিট 1 ছিল।

একইভাবে RIGHT SHIFT অপারেশন করা হলে যতঘর বলা থাকে নাম্বারটির বিটগুলো ততঘর ডানে সরে যায়। LEFT SHIFT এ যেমন বামের বিটগুলো হারিয়ে যায়, RIGHT SHIFT এ ডানের বিটগুলো হারিয়ে যায় এবং বামে 0 যোগ হতে থাকে।

 x = 45 >> 2;

45 = 0010 1101
 x = 0000 1011 => 11 // দুইঘর ডানে শিফট করার পর

এই হল বিট-ওয়াইজ অপারেটর সম্পর্কিত ধারণা, কোনটি কিভাবে কাজ করে। তবে এখানেই শেষ নয়। এই অপারেটরগুলোকে নানা ভাবে নানা কৌশলে কাজে লাগিয়ে অনেক সহজেই বিভিন্ন হিসাব সম্পন্ন করা যায়। এতে প্রোগ্রামের রানটাইমও অনেকখানি কমে যায় যেটি কিছু কিছু ক্ষেত্রে এসিএম প্রোগ্রামিং এ TLE এবং AC এর মধ্যে পার্থক্য করে দিতে পারে।

পরবর্তি পর্বঃ বিট-ওয়াইজ নিয়ে খেলা!