Site icon Trickbd.com

C/C++ এ Bitwise Operators সম্পর্কে যা কিছু জানার আছে – part 2

আসসালামু আলাইকুম। আশা করি সবাই ভালো আছেন।

এই পোস্টটি C/C++ এ Bitwise Operators নিয়ে করা আমার আগের পোস্টটির ২য় পার্ট। আশা করি ১ম পার্ট সবাই পড়ে আসছেন। না পড়লে চাইলে এখান থেকে পড়ে নিতে পারেন; আগের পোস্ট না পড়লেও এই পোস্ট না বোঝার কিছু নাই।

তো আজকে আমরা ৬ টি Bitwise Operator এর বাকি তিনটি শিখবো। আমরা আগের পার্ট এ Bitwise AND(&), Bitwise OR(|) এবং Bitwise XOR(^) Operator শিখছি। এই পার্ট এ আমরা Bitwise Left Shift(<<), Bitwise Right Shift(>>) এবং Bitwise NOT(~) operator শিখবো।

তো চলুন শুরু করি।

Bitwise ‘Left shift’ Operator:

Bitwise Left shift Operator তার বাম পাশের অপারেন্ড(অর্থাৎ বাম পাশে যেই ভ্যালু থাকবে) এর বাইনারি ফর্ম নিয়ে কাজ করে। এই অপারেটর তার বাম পাশের অপারেন্ড এর বাইনারি ফর্মে যেই বিট গুলো থাকবে(0 ও 1) সেগুলোকে বাম দিকে শিফট করবে বা সরাবে। এই অপারেটর ডান পাশে আরও একটি অপারেন্ড নিবে, এবং সেই অপারেন্ড এর মান যা হবে বাম পাশের অপারেন্ড এর বাইনারি এর বিট গুলোকে তত ঘর বামে সরাবে।

যেমন: 5 এর বাইনারি ফর্ম 101 । 5 একটি integer type value/literal, তাই এর সাইজ সাধারণ ভাবে হবে 16-bit । এখন 101 এর 16-bit representation হবে এরকম:

0000000000000101

এখানে যদি এই ভ্যালুর বিট গুলোকে বাম দিকে শিফট করতে চাই তাহলে Bitwise Left shift Operator ব্যবহার করতে হবে। ধরেন আমি এই বিট গুলোকে বাম দিকে 4 ঘর শিফট করতে চাই। তাহলে সেই Bitwise শিফট অপারেশন এর রেজাল্ট হবে এরকম:

0000000001010000

নিচের ছবির মাধ্যমে এই জিনিসটি ভিজ্যুয়ালি বুঝতে পারবেন।

ছবিতে 14 কে 1 এবং 2 দ্বারা left shift করে দেখানো হয়েছে।

আপনি যখন যেকোনো একটি মান x কে y বিট (x << y) দ্বারা লেফট শিফট করবেন, তখন x-এর বামতম(leftmost) y সংখ্যক বিটগুলি হারিয়ে যায়, এগুলো অস্তিত্বের বাইরে চলে যায়।

Code Example:

#include<iostream>

using namespace std;

int main() {

int a = 5; //binary: 0000000000000101
int b = a << 4; //binary: 0000000001010000

cout << b;

return 0;

}

এখানে 5 এর বাইনারি এর বিট গুলোকে Left shift Operator এর সাহায্যে 4 ঘর বামে শিফট করা হয়েছে (কিভাবে তা উপরে বলা হইছে)। এর ফলে রেজাল্ট হিসেবে আমরা পাই:

0000000001010000; যা ডেসিমাল এ 80 নির্দেশ করে। তাই আউটপুট হিসেবে 80 প্রিন্ট হবে। অর্থাৎ, 5 << 4 = 80।

N.B. এই লেফট শিফট অপারেটর এর রেজাল্ট বের করার একটি ম্যাথেম্যাটিকাল ফর্মুলা জেনে রাখুন:

Left Operand × 2^(Right Operand) = Result

5 × 2⁴ = 80

তবে যদি আপনার লেফট শিফট এর জন্য বাইনারি এর কোনো একটি/একাধিক 1 বাম দিকে অস্তিত্বের বাইরে চলে যায়, তখন এই ফর্মুলা দিয়ে সঠিক মান বের হবে না। 0 হারিয়ে গেলে সমস্যা নাই কিন্তু 1 হারিয়ে গেলে সূত্র কাজ করবে না।

Bitwise ‘Right shift’ Operator:

Bitwise Right shift Operator তার বাম পাশের অপারেন্ড এর বাইনারির বিট গুলোকে ডান দিকে শিফট করবে বা সরাবে। এই অপারেটরও ডান পাশে আরও একটি অপারেন্ড নিবে, এবং সেই অপারেন্ড এর মান যা হবে বাম পাশের অপারেন্ড এর বাইনারির বিট গুলোকে তত ঘর ডানে সরাবে।

যেমন: 5 এর বাইনারি ফর্ম 101 ।

101 এর 16-bit representation:

0000000000000101

এখানে এই ভ্যালুর বিট গুলোকে ডান দিকে শিফট করতে Bitwise Right shift Operator ব্যবহার করতে হবে। ধরেন আমি এই বিট গুলোকে ডান দিকে 1 ঘর শিফট করতে চাই। তাহলে সেই Bitwise শিফট অপারেশন এর রেজাল্ট হবে এরকম:

0000000000000010 [একটি 1 ডান দিকে হারিয়ে যায়]

নিচের ছবির মাধ্যমে এই জিনিসটি ভিজ্যুয়ালি বুঝতে পারবেন।

ছবিতে 14 কে 1 এবং 2 দ্বারা right shift করে দেখানো হয়েছে।

Right shift Operator এর ক্ষেত্রেও, আপনি যখন যেকোনো একটি মান x কে y বিট (x >> y) দ্বারা রাইট শিফট করবেন, তখন x-এর ডানতম(rightmost) y সংখ্যক বিটগুলি হারিয়ে যায় অর্থাৎ অস্তিত্বের বাইরে চলে যায়।

Code Example:

#include<iostream>

using namespace std;

int main() {

int a = 5; //binary: 0000000000000101
int b = a >> 1; //binary: 0000000000000010

cout << b;

return 0;

}

এখানে 5 এর বাইনারি এর বিট গুলোকে Right shift Operator এর সাহায্যে 1 ঘর ডানে শিফট করা হয়েছে। এর ফলে রেজাল্ট হিসেবে আমরা পাই:

0000000000000010; যা ডেসিমাল এ 2 নির্দেশ করে। তাই আউটপুট হিসেবে 2 প্রিন্ট হবে। অর্থাৎ, 5 >> 1 = 2।

N.B. লেফট শিফট এর মত রাইট শিফট অপারেটর এর রেজাল্ট বের করার ও একটি ম্যাথেম্যাটিকাল ফর্মুলা আছে:

Left Operand ÷ 2^(Right Operand) = Result

5 ÷ 2¹ = 2.5

কি ভাবতেছেন? আউটপুট এ 2 আসলো আর ফর্মুলা তে 2.5? লেফট শিফট অপারেটরের মত রাইট শিফট অপারেটর এর ক্ষেত্রেও বাইনারি এর কোনো একটি/একাধিক 1 ডান দিকে অস্তিত্বের বাইরে চলে গেলে তখন এই ফর্মুলা দিয়ে সঠিক মান বের হবে না। 0 হারিয়ে গেলে সমস্যা নাই কিন্তু 1 হারিয়ে গেলে সূত্র কাজ করবে না।

এখন পর্যন্ত আমরা রাইট শিফট অপারেটর সম্পর্কে যা শিখলাম তা লেফট শিফটের মতই। তবে রাইট শিফট অপারেটর এর একটা ব্যতিক্রমধর্মী আচরণ আছে, যা লেফট শিফট থেকে আলাদা। চলুন জানি এটা কি।

রাইট শিফট অপারেশন এর ক্ষেত্রে যদি আপনার দেওয়া ভ্যালুর বাইনারি মানের Most Significant Bit(MSB)/Leftmost Bit অর্থাৎ সবচেয়ে বামে অবস্থিত বিট টি 0 না হয়ে 1 হয় তাহলে অপারেশন টি কিভাবে সম্পন্ন হবে টা নির্ভর করবে আপনার দেওয়া ভ্যালুর ডাটা টাইপের উপর। যদি আপনার ভ্যালুর টাইপ হয় int, তাহলে সবচেয়ে বামে অবস্থিত বিট টি তার sign নির্দেশ করে। সবচেয়ে বামের বিট 1 হলে নেগেটিভ সাইন আর 0 হলে পজিটিভ সাইন।[না জেনে থাকলে জেনে রাখুন, int টাইপের ক্ষেত্রে ভ্যারিয়েবল সবসময় signed হয়, অর্থাৎ int টাইপ বলতে signed int বোঝায়, যদি আপনি নেগেটিভ ভ্যালু নিয়ে কাজ করতে না চান তাহলে int এর আগে unsigned বসাতে হবে, অর্থাৎ unsigned int টাইপ। signed এর ক্ষেত্রে আগে signed বসাতে না হলেও unsigned এর ক্ষেত্রে বসাতে হবে] তাহলে আপনার দেওয়া মান যদি int টাইপের হয় আর যদি তার বাইনারি মানের সবচেয়ে বামের বিট টি 1 হয় তাহলে ডান পাশে শিফট করলে ফাঁকা জায়গাগুলো 1 দ্বারা পূর্ণ হবে(আগের সব ক্ষেত্রে 0 দ্বারা হয়েছে)। উদাহরণ দেখুন:

1110000000000000

এই মানকে যদি আমি 2 ঘর ডানে শিফট করি তাহলে এরকম হবে:

1111100000000000

এটার কোড এক্সাম্পল নিজে করে দেখুন।

Bitwise NOT operator:

Bitwise NOT operator আরেকটি bitwise operator যা তার ডানপাশে কেবল একটি operand নিয়ে কাজ করে। এই অপারেটর তার ডানপাশে অবস্থিত operand এর বাইনারি মানের বিট গুলোকে inverse বা উল্টা করে দেয়, অর্থাৎ 0 কে 1 এবং 1 কে 0 করে দেয়। এই NOT অপারেটর কে Complement Operator বা One’s Complement Operator ও বলা হয়।

যেমন: 5 এর বাইনারি মান 101। এর 16-bit representation:

0000000000000101

5 এর NOT অপারেশন এর রেজাল্ট হবে এরকম:

1111111111111010

ছবি:

4 এর Bitwise NOT অপারেশন

Code Example:

#include<iostream>

using namespace std;

int main() {

int a = 5; //binary: 0000000000000101
int b = ~a; //binary: 1111111111111010

cout << b;

return 0;

}

এখানে 5 কে NOT অপারেশন করা হয়েছে। ফলে রেজাল্ট হিসেবে পাওয়া যায়: 1111111111111010; যা ডেসিমালে -6 নির্দেশ করে। তাই আউটপুট হিসেবে -6 প্রিন্ট হবে। অর্থাৎ, ~5 = -6

N.B. NOT অপারেটর এর রেজাল্ট বের করার ও একটি ফর্মুলা আছে,

-(Operand + 1) = Result

-(5 + 1) = -6

2’s Complement:

অনেকেই NOT অপারেটর এর ক্ষেত্রে একটা কনফিউশনে ভোগেন, টা হলো অনেকেই মনে করেন NOT অপারেটর এর operand কে তার নেগেটিভ ভ্যালু তে কনভার্ট করে। এটা ভুল, NOT অপারেটর শুধু তার operand এর বাইনারি মানের বিট গুলোকে ইনভার্ট করে দেয়। আর এই ইনভার্শন প্রক্রিয়ার রেজাল্ট হিসেবে যেই মান পাওয়া যায় তা operand এর বিপরীত চিহ্ন বিশিষ্ট হয় এবং তার থেকে 1 বেশি/কম হয়(পজিটিভ ভ্যালু operand থাকলে 1 বেশি, আর নেগেটিভ ভ্যালুর থাকলে 1 কম)। যেমন 5 এর NOT= -6, 4 এর NOT= -5, -5 এর NOT= 4 এরকম। তবে আপনি যদি কোনো মানের ঋণাত্মক মান বাইনারি এর মাধ্যমে বের করতে চান, তাহলে 2’s Complement নামের একটি মেথড ইউজ করতে হবে। এটা এরকম: প্রথমে আপনাকে বাইনারির বিট গুলোকে invert করতে হবে। এরপর এর সাথে 1 যোগ করতে হবে(বাইনারির যোগ)। {এই যোগের ক্ষেত্রে আপনার হাতে যদি 1 থাকে(ওভারফ্লো) আর সেটা শেষ 16তম বিট যদি পার হয়ে যায় তাহলে সেটা বাদ দিতে হবে, অর্থাৎ সেটা কাউন্ট এ আসবে না।}

যেমন: 5 এর বাইনারি:

0000000000000101

Inversion:

1111111111111010

+                              1

—————————–

1111111111111011; এটা বাইনারি তে -5 নির্দেশ করে।

[বি:দ্র: এই ভ্যালু এর ডেসিমাল -5 হবে শুধুমাত্র signed int টাইপের জন্য। unsigned int এর ক্ষেত্রে এর মান হবে: 65531]

তো আজকে এই পর্যন্তই। পোস্ট অনেক বড় হয়ে গেলো। সবাই ভালো থাকেন, বাবা মাকে সম্মান করেন আর ফিলিস্তিনে আমাদের ভাইদের জন্য দোয়া করেন। ধন্যবাদ।