অপারেটর আছে দুই ধরণেরঃ লজিকাল এবং বিটওয়াইজ। লজিকাল অপারেটর গুলো হল &&, || এবং !… আর বিটওয়াইজ অপারেটরগুলো হল সেই সব অপারেটর যারা বিটের উপর কাজ করে।
কম্পিউটারে সবকিছুই সংরক্ষিত থাকে দ্বিমিক সংখ্যা হিসেবে। কম্পিউটার ০ এবং ১ ছাড়া কিছুই বুঝে না। এই প্রতিটি ০ কিংবা ১ হল এক একটি বিট। বিটওয়াইজ অপারেটরগুলো এইসব ০ এবং ১-এর প্রতিটির উপর আলাদা আলাদা ভাবে কাজ করে।
বিটওয়াইজ অপারেটরগুলো বেশ গুরুত্বপূর্ণ, কারণ গুণ-ভাগের বদলে এরা সরাসরি বিটের উপর কাজ করায় রানটাইম কমে যায়। এর ফলে অনেকক্ষেত্রেই TLE বদলে AC হয়ে যেতে পারে, আবার AC-র মাঝেও র্যাংক-এ আসতে পারে বিশাল পার্থক্য। 😀
/* তবে শুরু করার আগে একটা কথা বলে রাখি। এই অপারেটরগুলো শুধুমাত্র integer-এর উপর কাজ করবে, কোনো float কিংবা double-এর জন্য না। কারণ float কিংবা double সম্পূর্ণ ভিন্নভাবে মেমরিতে সংরক্ষিত থাকে। */
এ ধরণের অপারেটর আছে ছয়টাঃ
(১) & (AND)
(২) | (OR)
(৩) ^ (XOR অথবা, Exclusive OR)
(৪) << (LEFT SHIFT)
(৫) >> (RIGHT SHIFT)
(৬) ~ (NOT অথবা, Completment)
এদের মধ্যে প্রথম ৫ টা হল বাইনারি অপারেটর, যারা দুইটি সংখ্যার উপর কাজ করবে। আর শেষেরটা হল Unary অপারেটর, যা একটি সংখ্যার উপরই কাজ করবে।
প্রথমেই আসি AND-এর কাছে
বিট-ওয়াইজ AND(কোডে লিখবো &) অনেকটা লজিকাল AND (যাকে আমরা লিখি &&)-এর মতই কাজ করে। শুধু পার্থক্যটা হল এটি প্রতিটা বিটের উপর আলাদা আলাদাভাবে কাজ করে। ০ মানে হল মিথ্যা আর ১ মানে সত্য। তাহলে এই অপারেটর ১ রিটার্ন করবে শুধু তখনই যখন দুইটা সংখ্যার একই পজিশনের বিট ১ হবে, নাহলে এটি রিটার্ন করবে ০। একটি উদাহারণ দিয়ে বুঝানোর চেষ্টা করা যাক।
ধর তোমার কাছে দুইটি ৮ সাইজের দুইটা unsigned ভ্যারিয়েবল আছে। সাইজ ৮ মানে যার বিট রয়েছে ৮ টি।
ধরা যাক, সংখ্যা দু’টি ৬৯ (a) এবং ৪২ (b)। এদেরকে বাইনারিতে রূপান্তর করলে পাবেঃ
69 = 01000101 /* প্রথম বিটটি শূন্য রয়ে গেছে, তার মানে এই না যে সেটা না লিখলেও হবে! */
42 = 00101010
এখন আমরা যদি এদের উপর বিটওয়াইজ AND চালিয়ে দিয়ে ভ্যালুটা AND নামের একটি ভ্যারিয়েবলে রাখতে চাই, তাহলে আমরা লিখবঃ
AND = a & b;
এটিকে আমরা এমনভাবেও লিখতে পারিঃ x = 42 & 69
কোনটা আগে, কোনটা পরে, সেটা কোনো ব্যাপার না!
এখন এই বিটওয়াইজ AND চালিয়ে দেওয়ার ফলে যা হবে তা হলঃ
69 = 0100 0101
& 42 = 0010 1010
_______________
0000 0000
Oops! 😀 শূণ্য হয়ে গেল, কারণ এদের কোনো বিটেই দুই সংখ্যাতেই শূন্য ছিল না! চল এবার অন্য দুইটা বাইনারি সংখ্যা নিয়ে চেষ্টা করা যাক! আমি খুব অলস তাই ডেসিম্যাল নাম্বার নিয়ে তা আবার বাইনারি করে দেখতে চাচ্ছি না বিটে মিল আছে কি না! 😀
1010 1100
&0110 0100
_________
0010 0100
এবার এখানে কি হল সেটা বুঝার চেষ্টা করা যাক।
সংখ্যা দুইটার প্রথম বিটে একটাতে আছে ১, আরেকটাতে শূন্য, তাহলে রিটার্ন করবে ০।
দ্বিতীয় বিটেও একই কাহিনী। তৃতীয় বিটে দুইটা নাম্বারেই আছে ১, তাহলে রিটার্নও করবে ১!
বাকি বিটগুলা নিজে ব্যাখ্যা করে ফেল। হোমওয়ার্ক! 😀
এবার পালা বিটওয়াইজ OR-এর!
আমরা && আর ||-এর পার্থক্য তো ইতোমধ্যেই জানি। তাহলে এখন বিটওয়াইজ AND আর OR-এর মধ্যে পার্থক্যও বুঝতে পারার কথা। না বুঝলেও সমস্যা নেই, আমি আছি কি জন্যে? 😉
বিটওয়াইজ OR চালানোর জন্য আমরা লিখবোঃ
OR = a | b;
AND অপারেটর 1 রিটার্ন করতো ওই বিটে দুইটা সংখ্যাতেই 1 থাকলে। আর OR অপারেটর একটু দয়ালু। তাই সে ওই বিটটিতে দুইটা সংখ্যার যে কোনো একটাতেই 1 থাকলে রিটার্ন করে দিবে 1! আমাদের আগের উদাহারণটি দেখা যাক।
1010 1100
|0110 0100
_________
1110 1100
এখানে প্রথম বিটে দ্বিতীয় সংখ্যাতে 0 থাকলেও প্রথমটিতে 1 আছে, তাই রিটার্ন করবে 1…
আবার চতুর্থটিতে দুইটা সংখ্যাতেই শূন্য, তাই রিটার্নও করবে শূন্য। বাকি বিটগুলা হোমওয়ার্ক! 😀
আচ্ছা, ৪২ আর ৬৯ কে AND করলে পেয়েছিলাম 00000000… বলতো OR করলে কত পাব?
এবার যাব আমরা Exclusive OR-এর কাছে 🙂
বিটওয়াইজ OR-এর দূরসম্পর্কের আত্মীয় হল বিটওয়াইজ XOR. তবে এর মাথায় একটু সমস্যা। এটি দুইটা সংখ্যার যেকোনো একটিতে একই বিটে 1 থাকলে 1 রিটার্ন করে ঠিকই। কিন্তু সংখ্যার দু’টির দুইটিতেই 1 থাকলেই রিটার্ন করে 0!
/* আবার বলি! এটি 1 রিটার্ন করবে তখনই যখন নির্দিষ্ট বিটটিতে যেকোনো একটি সংখ্যাতে 1 থাকে। দুইটিতেই 1 থাকলে রিটার্ন করবে 0! */
আর XOR করার জন্য লিখতে হয় এভাবেঃ
XOR = a ^ b;
আমাদের আগের সেই দুইটা বাইনারি নাম্বারের কাছে ফিরে যাই আবার!
1010 1100
^0110 0100
_________
1100 1000
এখানে প্রথম, দ্বিতীয়, তৃতীয় আর পঞ্চম বিটে যেকোনো একটিতে 1 আছে, তাই রিটার্ন করেছে 1. আর ষষ্ট বিটে দুইটাতেই আছে 1, তাই রিটার্ন করে দিয়েছে 0. আর বাকি বিটগুলাতে তো কোনোটাতেই 1 নাই, ওগুলাতে রিটার্ন অবশ্যই 0! 😀
এবার দুই ভাই Left Shift আর Right Shift-এর পালা!
এই দুইটা অপারেটরগুলার কাজ খুবই সহজ সরল ধরণের। এরা যেকোনো একটি সংখ্যার পুরা বিট প্যাটার্নকে নির্দিষ্ট সংখ্যক ঘর ডানে বা বামে সরিয়ে দেয়!
Right Shift লিখতে হয় এভাবেঃ
daaneshor = a >> numberofshifts;
numberofshifts হল তুমি যতবার শিফট করতে চাও, সেটা। ধরা যাক এই উদাহারনে এর মান 7. তাহলে,
প্রথম বার শিফট করে আমরা পাবঃ 01010101
দ্বিতীয় বার শিফট করে আমরা পাবঃ 00101010
তৃতীয় বার শিফট করে আমরা পাবঃ 00010101
চতুর্থ বার শিফট করে আমরা পাবঃ 00001010
বাকিগুলা হোমওয়ার্ক! 😀
আবার Left Shift করার জন্য লিখতে হয়ঃ
baayeplastic = a << numberofshifts;
প্রথম বার শিফট করে আমরা পাবঃ 01101110
/* একটা জিনিস খেয়াল কর, যেহেতু আমরা ডাটা টাইপের সাইজ রেখেছিলাম ৮, তাই এখানে সবচেয়ে বামের একটি বিটে 1 ছিল, যা লেফট শিফট করার পর হারিয়ে যাচ্ছে। এটা খুবই চিন্তার বিষয়। তাই লেফট শিফট ব্যবহার করার সময় বিশেষভাবে খেয়াল রাখতে হবে ডাটা টাইপের সাইজের উপর! */
দ্বিতীয় বার শিফট করে আমরা পাবঃ 11011100
তৃতীয় বার শিফট করে আমরা পাবঃ 10111000
চতুর্থ বার শিফট করে আমরা পাবঃ 01110000
বাকিগুলা হোমওয়ার্ক! 😀
সবশেষে NOT
আগেই বলেছি এটা একটা Unary অপারেটর। অর্থাৎ এর জন্য আগের গুলার মত দুইটা সংখ্যা লাগবে না। NOT করার জন্য লিখতে হয় এভাবেঃ
NOT = ~69;
এই অপারেটর যা করে তা হল, বিটে 0 থাকলে 1 করে দেয় আর 1 থাকলে তাকে 0 করে দেয়! তাহলে আমরা 69-এর বাইনারি রিপ্রেজেন্টেশন 01000101-এর কমপ্লিমেন্ট কি হয় তা দেখি।
প্রথম বিটের 0 হয়ে যাবে 1
দ্বিতীয় বিটের 1 হয়ে যাবে 0
শেষ পর্যন্ত আমরা যে সংখ্যাটা পাব, তা হল 10111010, যার ডেসিম্যাল রিপ্রেজেন্টেশন হল 186!
আজকের মত এখানেই শেষ। বিটওয়াইজ অপারেটরগুলো নিয়ে অনেক মজার জিনিস করা যায়। সেগুলো থাকবে আমাদের পরের পর্বে! 🙂
অনুভুতি বেশ মজার । তবে কথা হোল “Exclusive OR কিন্তু সংখ্যার দু’টির দুইটিতেই 1 থাকলেই রিটার্ন করে 0! ” এইখানে ” ! ” কি ফ্যাক্টরিয়াল ? 😀
আরে না,ফ্যাকটরিয়াল সাইনটা জাস্ট এক্সপ্রেশনের জন্য ব্যবহার করা হইছে :p
Helpful 😀
int x = 69;
int y = ~x; (bitwise NOT x)
printf(“%d\n”,y);
This code prints -70 in my codeblocks.
For any value of x, it prints – (x+1).
What did I do wrong?
int x = 69;
int y = ~x;
printf(“%d\n”,y);
This code prints -70 in my codeblocks.
For any value of x, it prints – (x+1).
What did I do wrong?