ধরুন, একটা কাগজে "Hello" লিখে আপনার সামনে ধরলাম। আপনি বুঝবেন এটা পাঁচটা অক্ষর, একটা শব্দ। যদি একটা বিড়ালের ছবি দেখাই, বুঝবেন এটা একটা প্রাণীর ছবি। MP3 চালালে শুনবেন একটা গান।
কিন্তু কম্পিউটার এগুলোর কোনোটাই দেখে না। তার কাছে "Hello" শব্দটা, বিড়ালের ছবি আর আপনার প্রিয় গান — সবই শেষ পর্যন্ত bit-এর pattern।
আগের আর্টিকেলে দেখেছি, একটা bit হলো logical ০ বা ১, যাকে hardware কোনো physical বৈদ্যুতিক অবস্থা দিয়ে ধরে রাখে।
কিন্তু বাস্তব জগতের একটা অক্ষর, একটা রঙ, একটা সুর — এগুলো memory-তে ঢোকার আগে কীভাবে ০ আর ১-এ রূপ নেয়? সেই translation-এর গল্পটাই আজকের বিষয়।
প্রথম নিয়ম: সবকিছুকে bit-এ প্রকাশ করতে হবে
Computer "Hello" শব্দটা, লাল রঙ বা একটা সুরকে যেমন আছে তেমন রাখতে পারে না। তথ্যটাকে আগে একটা নির্দিষ্ট নিয়মে encode করতে হয় — এমন একটা রূপে, যা শেষ পর্যন্ত bit-এর pattern হিসেবে লেখা যায়।
বেশিরভাগ ক্ষেত্রে মাঝখানে একটা সংখ্যা থাকে। প্রতিটা অক্ষর পায় একটা পরিচয় নম্বর, ছবির প্রতিটা বিন্দু পায় উজ্জ্বলতার নম্বর, শব্দ পায় মাপা উচ্চতার নম্বর। তারপর সেই সংখ্যা bit হয়।
তবে একটা কথা মনে রাখবেন: একটা bit pattern-এর অর্থ কী, সেটা নির্ভর করে কোন নিয়মে সেটা পড়া হচ্ছে। এই কথায় article-এর শেষে ফিরব।
তাহলে প্রথম প্রশ্ন — সংখ্যা নিজেই কীভাবে binary হয়?
সংখ্যা থেকে binary
মানুষ base-10 বা ডেসিমাল সিস্টেম ব্যবহার করে। কেন? সম্ভবত কারণটা সহজ — আমাদের ১০টা আঙুল আছে। ০ থেকে ৯ পর্যন্ত ১০টা digit, আর প্রতিটা column-এর মান ১০-এর power হিসেবে বাড়ে — একক, দশক, শতক, সহস্র।
আগের article-এ দেখেছি, digital circuit দুটো অবস্থাকে সবচেয়ে নির্ভরযোগ্যভাবে আলাদা করতে পারে। তাই কম্পিউটারে ব্যবহার হয় base-2 বা binary। এখানে digit শুধু দুইটা (০ আর ১), আর প্রতিটা column-এর মান ২-এর power হিসেবে বাড়ে — ১, ২, ৪, ৮, ১৬, ৩২ — এভাবে।
কেন ঠিক ২-এর power? প্রতিটা নতুন bit সম্ভাবনার সংখ্যা দ্বিগুণ করে দেয়। এক bit-এ দুইটা possibility (০ অথবা ১)। দুই bit-এ চারটা (০০, ০১, ১০, ১১)। তিন bit-এ আটটা, চার bit-এ ষোলটা। এভাবেই সম্ভাবনা exponentially বাড়তে থাকে।
তাহলে এই কলামগুলো দিয়ে সংখ্যা বানাবো কীভাবে? নিয়মটা সহজ: যে যে কলামের মান যোগ করতে হবে, সেগুলোকে 1 করে দিন, বাকিগুলোকে 0। নিচের যন্ত্রে নিজেই ১৩ বানিয়ে দেখুন:
উদাহরণ হিসেবে ১৩ ধরা যাক। কলামগুলো ৮, ৪, ২, ১। ১৩ বানাতে লাগবে একটা ৮, একটা ৪ আর একটা ১ (৮ + ৪ + ১ = ১৩)। ২ লাগছে না। তাই বাইনারিতে ১৩ হলো 1101।
1101₂ = (1×8) + (1×4) + (0×2) + (1×1) = 13₁₀
পজিটিভ সংখ্যা তো বুঝলাম, কিন্তু মাইনাস (−) চিহ্ন রাখা হবে কীভাবে? Memory-তে প্লাস-মাইনাস বলে কিছু নেই — আছে শুধু bit।
প্রথম দেখায় মনে হতে পারে, একদম বামের bit-টাকে (Most Significant Bit) চিহ্নের জন্য রেখে দিলেই হয় — 0 মানে প্লাস, 1 মানে মাইনাস। এই পদ্ধতির নাম Sign-Magnitude। কিন্তু এতে একটা বড় খুঁত আছে: +0 আর −0 নামে শূন্যের দুটো আলাদা রূপ তৈরি হয় (৮-bit-এ 00000000 আর 10000000)। গণিতে শূন্যের কোনো চিহ্ন নেই, আর দুটো শূন্য থাকা মানে যোগ-বিয়োগের circuit অকারণে জটিল হওয়া।
এর সবচেয়ে চমৎকার, hardware-বান্ধব সমাধান হলো Two's Complement।
নেগেটিভ সংখ্যা বের করার নিয়মটা সহজ: পজিটিভ রূপের প্রতিটা bit উল্টে দিন (0-কে 1, 1-কে 0 — যাকে বলে One's Complement), তারপর তার সাথে 1 যোগ করুন।
এর বড় সুবিধা: বিয়োগকে যোগ হিসেবে করা যায় — A − B মানে A + (−B)। ফলে একই adder circuit দিয়েই যোগ-বিয়োগ দুটো চলে; ALU-কে শুধু সামান্য control দিয়ে বলে দিতে হয় এখন যোগ না বিয়োগ। চিহ্ন সামলানোর জন্য আলাদা কোনো circuit লাগে না।
সংখ্যা তো হলো। কিন্তু বাস্তবে তো শুধু সংখ্যা লেখেন না। চ্যাটবক্সে "Hello" লিখলে সেই অক্ষরগুলো কীভাবে binary হয়?
তথ্য যদি হয় text
Text-কে binary বানানোর ট্রিকটা সহজ — প্রতিটা অক্ষরের জন্য একটা করে নির্দিষ্ট সংখ্যা বরাদ্দ করা।
Computing-এর একেবারে শুরুর দিকে এই standard-এর নাম ছিল ASCII। প্রতিটা ইংরেজি অক্ষর, digit, punctuation — সবার জন্য একটা করে ৭-bit code:
বড় হাতের A → 65 → 01000001 ছোট হাতের a → 97 → 01100001
ইংরেজির জন্য ASCII ঠিকঠাকই কাজ করত। সমস্যা শুরু হলো অন্য ভাষা বা emoji-র প্রয়োজন পড়ায়। ৭ bit-এ সর্বোচ্চ ১২৮টা character-এর জায়গা — শুধু বাংলা বর্ণমালার জন্যই যথেষ্ট না, পৃথিবীর হাজার হাজার ভাষার কথা তো বাদই দিলাম।
শুরুর দিকে যখন Python বা C দিয়ে database-এ বাংলা input নেওয়া হতো, প্রায়ই screen-এ অদ্ভুত হিজিবিজি character দেখাত। প্রোগ্রামাররা ভাবত — লিখলাম বাংলা, screen-এ কেন এই garbage?
Computer আসলে বাংলা character-কে ভুল dictionary দিয়ে decode করার চেষ্টা করছিল। ASCII-র dictionary-তে বাংলা নেই। ছিলই না।
এর সমাধান Unicode — একটা international standard, যা পৃথিবীর নানা লিপির অক্ষর আর symbol-এর জন্য একটা করে code point, মানে পরিচয় নম্বর, ঠিক করে দেয়।
এখানে একটা সূক্ষ্ম পার্থক্য আছে, যেটা অনেকেই ধরতে পারে না, কারণ Unicode আর UTF-8 প্রায়ই একই অর্থে বলা হয়। কিন্তু এরা এক জিনিস না। Unicode বলে দেয় প্রতিটা অক্ষরের পরিচয় কী। সেই পরিচয় actual byte হিসেবে কীভাবে লেখা হবে — সেটা বলে UTF-8।
UTF-8 হলো variable-length encoding। ইংরেজি অক্ষরের জন্য ১ byte — ASCII-র সাথে হুবহু মিলে যায়। বাংলা অক্ষরের জন্য সাধারণত ৩ byte, আর অনেক emoji-র জন্য ৪ byte।
Text-এর গল্প এতটুকুই। তথ্যের টুকরোটা যদি অক্ষর হয়, এতক্ষণে সে byte হয়ে memory-তে ঢোকার জন্য প্রস্তুত।
কিন্তু তথ্য যদি ছবির একটা অংশ হয়?
তথ্যের টুকরো যদি হয় image
যেকোনো digital ছবিকে খুব কাছ থেকে দেখলে দেখা যাবে, এটা আসলে লক্ষ লক্ষ ক্ষুদ্র বিন্দুর সমষ্টি। প্রতিটা বিন্দুকে বলে pixel।
Screen-এ রঙ তৈরিতে ব্যবহার হয় RGB model — প্রতিটা pixel তিনটা primary color-এর মিশ্রণ: Red, Green, Blue।
প্রতিটা color কতটা উজ্জ্বল, সেটা বলে ০ থেকে ২৫৫-এর একটা মান। ২৫৫ মানে সর্বোচ্চ উজ্জ্বলতা, ০ মানে সম্পূর্ণ বন্ধ। ২৫৫ পর্যন্ত রাখতে প্রতি channel-এ ৮ bit (১ byte) লাগে।
তাহলে একটা pixel-এর রঙে মোট ৮ + ৮ + ৮ = ২৪ bit। একেই বলে 24-bit color depth। নিচের যন্ত্রে যেকোনো pixel-এ চাপ দিয়ে তার binary রূপ দেখুন:
Computer এই pixel মানগুলো row-by-row সাজিয়ে পুরো ছবিটাকে একটা লম্বা bit sequence হিসেবে রাখে।
এবার একটা হিসাব। 1920×1080 একটা ছবিতে:
1920 × 1080 = 2,073,600 pixel
× 24 bit ≈ 49.8 million bit
≈ 6.2 MBঅর্থাৎ compress না করা (raw) অবস্থায় এমন একটা ছবির জন্য সত্যিই প্রায় ৬.২ MB লাগে। অথচ phone-এর একটা JPEG ছবি প্রায়ই এর চেয়ে অনেক ছোট file। কেন? কারণ সেটা compressed — সেই গল্প একটু পরে।
Image-এর গল্পও শেষ। কিন্তু কানে যা শুনি? বাতাসে ভেসে আসা একটা সুর তো কোনো অক্ষর না, কোনো pixel-ও না। সেটাকে bit বানাব কীভাবে?
তথ্যের টুকরো যদি হয় sound
শব্দ মূলত একটা continuous pressure wave — বাতাসের কাঁপুনি। কিন্তু computer রাখতে পারে শুধু আলাদা আলাদা (discrete) সংখ্যা।
তাহলে continuous জিনিসকে discrete করা যায় কীভাবে?
একটা analogy। ধরুন আপনি একটা দৌড়ের video করছেন। প্রতি সেকেন্ডে একটাই ছবি তুললে video খাপছাড়া লাগবে — এক মুহূর্তে দৌড়বিদ এখানে, পরের মুহূর্তে হঠাৎ অনেক দূরে। কিন্তু প্রতি সেকেন্ডে ৬০টা ছবি তুললে সেটা smooth চলমান video হয়ে যায়।
শব্দের ক্ষেত্রেও তাই। পুরো wave-টা ধরে রাখা হয় না — বরং খুব দ্রুত অনেকগুলো "ছবি" তোলা হয়। প্রতিটা "ছবি" মানে: ঠিক এই মুহূর্তে wave-টার উচ্চতা (amplitude) কত।
এই প্রক্রিয়ার নাম sampling। নিচের যন্ত্রে rate কমিয়ে-বাড়িয়ে দেখুন wave-টা কতটা বিশ্বস্তভাবে ধরা পড়ে:
একটা audio কতটা নিখুঁত হবে, সেটা নির্ভর করে দুটো জিনিসের ওপর।
Sample rate: প্রতি সেকেন্ডে কতবার wave-এর উচ্চতা মাপা হচ্ছে। CD quality audio-র standard 44,100 Hz — প্রতি সেকেন্ডে ৪৪,১০০ বার measurement।
Bit depth: প্রতিটা measurement কতগুলো সম্ভাব্য মানের একটা হতে পারবে। ১৬ bit মানে ৬৫,৫৩৬টা আলাদা level। Bit বেশি হলে প্রতিটা measurement আসল উচ্চতার আরও কাছাকাছি বসে।
সহজ করে বললে — sample rate ঠিক করে কত ঘনঘন মাপা হচ্ছে, আর bit depth ঠিক করে প্রতিবার কত সূক্ষ্মভাবে মাপা হচ্ছে।
তারমানে তথ্যের টুকরো এখন আর অক্ষর, pixel বা শব্দ না — সব রূপেই সে এখন সংখ্যা, আর সংখ্যা মানেই bit।
কিন্তু এখানে একটা সমস্যা আছে।
সমস্যা — সব কিছুই বিশাল
Text, image, sound-এর প্রতিটা কণা যদি এভাবে হুবহু লিখে রাখা হয়, file size হবে ভয়াবহ:
3-minute গান (CD quality) → ~30 MB (raw) 1080p ছবি → ~6 MB (raw) 1-hour 4K video (30 fps) → ~2.7 TB (raw)
এত বড় file-এর ভার internet সহ্য করতে পারবে না। Instagram-এ ছবি upload করতে ঘণ্টা লেগে যাবে। YouTube video load-ই হবে না।
সমাধান হলো compression — ছোট করে ফেলা। আর এই magic আসলে দুই ধরনের।
Compression: চতুরভাবে ছোট করা
Lossless compression-এ কিছুই হারায় না। File compress করলাম, পরে decompress করলাম — হুবহু original ফিরে পেলাম, একটাও bit বদলাল না।
এটা কীভাবে সম্ভব? একটা প্রচলিত কৌশল হলো data-র মধ্যে repetition বা pattern খুঁজে বের করা। সবচেয়ে সরল উদাহরণ দিয়ে শুরু করা যাক — নিচের জিনিসটা লেখার সহজ উপায় কী?
AAAAAAAAAAAAAAAAAA
আপনি হয়তো বলবেন — "18 × A"।
দুটোই একই কথা, কিন্তু দ্বিতীয়টা অনেক ছোট। এটাই Run-Length Encoding (RLE)-এর আইডিয়া: পর পর একই জিনিস থাকলে বারবার না লিখে শুধু "কতবার" আর "কী" লেখা। কোনো ছবিতে পর পর ৫০টা সাদা pixel থাকলে RLE লিখবে 50 × White — ৫০টা আলাদা মানের বদলে একটা জোড়া।
আরেকটা technique হলো Huffman coding। ধরুন আপনি আর আপনার বন্ধু প্রতিদিন ১০০ বার একটা phrase লেখেন — "ঠিক আছে"। দুজন মিলে যদি আগে থেকে ঠিক করে রাখেন একটা ⭐ মানে "ঠিক আছে", তাহলে একই তথ্য অনেক কম জায়গায় প্রকাশ করা যায়।
Huffman-এর আইডিয়া ঠিক এমন: file-এ সবচেয়ে বেশিবার আসা data-কে সবচেয়ে ছোট code দাও, কম আসা data-কে বড় code। মোট size তখন কমে যায়। (পুরো algorithm-এ tree, priority queue আসে — আপাতত এই intuition-টুকুই যথেষ্ট।)
Developer হিসেবে যখন browser-এ HTML, CSS, JS পাঠান, server সাধারণত Gzip বা Brotli দিয়ে সেটা ছোট করে নেয়। এখানে lossless-ই দরকার — একটা semicolon হারালেই code ভেঙে পড়বে, আর lossless-এ decompress করলে হুবহু একই byte ফিরে আসে।
Lossy compression অন্য জিনিস। এখানে কিছু তথ্য ইচ্ছা করে বাদ দেওয়া বা আনুমানিক (approximate) করা হয় — বেছে বেছে সেই অংশ, যেটা হারালে মানুষের চোখ-কানে ক্ষতিটা সবচেয়ে কম ধরা পড়ে। তবে ক্ষতিটা সবসময় পুরোপুরি অদৃশ্য, তা না।
Sound-এ: MP3 মানুষের শোনার সীমাবদ্ধতা কাজে লাগায়। যে অংশ মানুষ প্রায় শুনতেই পায় না, সেটা বাদ। একটা জোরালো শব্দের পাশে একই মুহূর্তে একটা হালকা শব্দ থাকলে কান হালকাটা ধরতে পারে না — তাই সেটাও বাদ।
Image-এ: JPEG চোখের বৈশিষ্ট্য কাজে লাগায়। আমাদের চোখ রঙের চেয়ে আলোর তারতম্য বেশি ভালো ধরে। তাই রঙের তথ্য কিছুটা কমিয়ে দিলে চোখ টের পায় না, কিন্তু file অনেক ছোট হয়।
Video-তে: এক frame থেকে পরের frame-এ যে অংশ বদলায় না (যেমন background), সেটা নতুন করে না রেখে আগের frame থেকে reference নেওয়া হয়। H.264, H.265, AV1 — এরা সবাই এই আইডিয়া কাজে লাগায়।
সহজ rule of thumb — হুবহু original দরকার হলে lossless; চোখ-কানে প্রায় একই লাগলেই চলবে এমন হলে lossy। তাই code, text, database-এ lossless। আর media মানেই lossy, তা-ও না — PNG ছবি বা FLAC গান lossless।
CPU নিজে থেকে এসব meaning জানে না
এই জায়গায় একটা গুরুত্বপূর্ণ ব্যাপার আছে।
ধরুন memory-তে কোথাও এই আটটা bit আছে: 01000001। Hardware এর দিকে তাকিয়ে নিজে থেকে বলবে না "এটা A"। আবার এটা জন্মগতভাবে ৬৫ সংখ্যাও না।
একই bit pattern ভিন্ন context-এ ভিন্ন জিনিস বোঝাতে পারে — unsigned সংখ্যা হিসেবে পড়লে ৬৫, ASCII বা UTF-8 text হিসেবে পড়লে "A", আর কোনো ছবির format-এর ভেতরে হয়তো একটা রঙের channel-এর মান।
কোনটা ঠিক, সেটা ঠিক করে program কোন instruction চালাচ্ছে আর data-টা কোন format-এ রাখা আছে। নিচে একই আট bit, তিনটা ভিন্ন lens-এ:
তার মানে CPU অকেজো, তা না — সে machine instruction-এর নির্দিষ্ট নিয়ম নিখুঁতভাবে মেনে চলে। কিন্তু কোনো bit pattern "বাংলা", "বিড়াল" নাকি "৬৫" — সেটা সে নিজে থেকে আবিষ্কার করে না। Bit নিজে কোনো মানুষ-বোধ্য অর্থ বহন করে না; অর্থ আসে representation আর context থেকে।
পুরো গল্পটা একবার
এবার "Hello"-কে উদাহরণ হিসেবে নিই। আপনি একটা source file-এ লিখলেন:
const message = "Hello";
File-টা UTF-8-এ save হলে "Hello"-র পাঁচটা অক্ষর file-এ পাঁচটা byte হিসেবে বসে (hex-এ লেখা):
H e l l o 48 65 6C 6C 6F
- প্রতিটা byte আটটা bit — যেমন H মানে
01001000 - File disk-এ থাকুক বা RAM-এ, সেই bit-গুলো সেখানকার hardware-এর physical অবস্থা হিসেবে থাকে — ঠিক কী ধরনের অবস্থা, সেটা storage technology-র ওপর নির্ভর করে
- JavaScript engine byte-গুলো পড়ে code parse করে, আর string-টাকে নিজের ভেতরের একটা representation-এ রাখে — সেটা হুবহু UTF-8 হতেই হবে, এমন না
- একই text network-এ পাঠালে আরও কিছু layer যোগ হতে পারে — যেমন ইচ্ছামতো Gzip compression
একটা অক্ষরের যাত্রাটা আলাদা করে দেখলে:
"H" ← মানুষের অক্ষর ↓ Unicode U+0048 ← code point ↓ UTF-8 0x48 ← byte ↓ 01001000 ← bit ↓ physical বৈদ্যুতিক অবস্থা
পুরো পথে representation বদলেছে বারবার। কখনো শুধু রূপ বদলায় আর original হুবহু ফিরে পাওয়া যায় — যেমন lossless compression-এ। কখনো ইচ্ছা করেই আনুমানিক করা হয় — যেমন lossy compression-এ। দুই ক্ষেত্রেই লক্ষ্য একটাই: তথ্যের কাজের অর্থটুকু যতটা সম্ভব ধরে রাখা।
- কম্পিউটার ছবি "দেখে" না, গান "শোনে" না — সব তথ্যকে আগে একটা নির্দিষ্ট নিয়মে bit-এ encode করতে হয়।
- প্রতিটা ধরনের তথ্যের নিজের নিয়ম আছে — text-এর জন্য Unicode + UTF-8, ছবির জন্য RGB pixel, শব্দের জন্য sampling।
- Compression দুই ধরনের — lossless-এ হুবহু original ফেরে, lossy-তে কিছু তথ্য বাদ বা আনুমানিক করা হয়।
- Meaning bit-এ থাকে না — একই bit pattern representation আর context ভেদে অক্ষর, সংখ্যা বা pixel।
- সংখ্যা, byte, bit আর voltage এক জিনিস নয় — একই তথ্যের ভিন্ন ভিন্ন স্তর।
অর্থ ↓ representation ← এই article bit pattern ↓ physical অবস্থা ← article ০১ stored state ↓ computation ← পরের article নতুন state → নতুন bit → অর্থ