সরাসরি পর্বে যান
পর্ব ০৪ · ৬২-এর মধ্যে · মডিউল ১

সংখ্যা পদ্ধতি — binary, octal, hexadecimal আর রূপান্তর

Number Systems — Positional Notation, Base Conversion Algorithms, Binary & Hex Arithmetic, BCD and Gray Code

Taught in Bangla, this lesson derives every base from one positional formula, proves Horner's method, repeated division and repeated multiplication for converting integers and fractions, predicts which fractions never terminate in binary, groups bits for octal and hex, counts bits and powers of two, explains KB versus KiB, does binary and hex arithmetic, and covers BCD, excess-3 and Gray code, with a step-by-step base-converter simulator and live C examples.

এই পর্বে যা শিখবেন

  • যেকোনো ভিত্তির সংখ্যা পদ্ধতির সাধারণ সূত্র, আর কেন hardware-এর জন্য ভিত্তি ২-ই সবচেয়ে সুবিধার
  • প্রতিটি রূপান্তর পদ্ধতি কেন কাজ করে — ছোট প্রমাণসহ, যাতে পরীক্ষায় শুধু নিয়ম নয়, যুক্তিও লিখতে পারেন
  • কোন দশমিক ভগ্নাংশ binary-তে শেষ হয় আর কোনটি হয় না, তা আগে থেকেই বলতে পারা
  • কত বিট লাগবে, KB আর KiB-এর পার্থক্য, আর binary-hex-এ সরাসরি যোগ-বিয়োগ-গুণ
  • BCD, excess-3 আর Gray code — কোথায় লাগে আর কীভাবে রূপান্তর হয়

১ · স্থানীয় মানের সংখ্যা পদ্ধতি — একটিই সূত্র

বাজারের ক্যালকুলেটরে ২৩৪৭ লিখলে আমরা কেউ ভাবি না এর মানে কী। কিন্তু ভেতরে একটি নিয়ম আছে: শেষ অঙ্ক ৭ মানে সাতটি এক, তার বাঁয়ের ৪ মানে চারটি দশ, তারপর তিনটি একশো, দুটি হাজার। অঙ্কটি কোন ঘরে বসেছে, তার ওপর নির্ভর করে তার মান। এই ধরনের লেখাকে বলে positional number system বা স্থানীয় মানের সংখ্যা পদ্ধতি।

আগে একটু ঝালিয়ে নিতে চান? একেবারে শুরু থেকে, অনেক ছবি আর সহজ উদাহরণসহ সংখ্যা পদ্ধতি দেখানো হয়েছে কম্পিউটার ফান্ডামেন্টালস কোর্সের সংখ্যা পদ্ধতির পর্বে। এখানে আমরা সেই ভিত্তির ওপর দাঁড়িয়ে আরও গভীরে যাব — সাধারণ সূত্র, প্রতিটি পদ্ধতির প্রমাণ, ভগ্নাংশের জটিলতা, binary-hex-এ সরাসরি হিসাব আর বিশেষ কোড।

যেকোনো ভিত্তি (radix বা base) r-এর জন্য নিয়মটি একই। অঙ্ক হতে পারে শুধু 0, 1, …, r−1 — মোট r টি চিহ্ন। ডান দিক থেকে ঘরগুলোর ওজন r0, r1, r2, … আর দশমিক বিন্দুর ডানে r−1, r−2, …

(dn−1 … d1 d0 . d−1 d−2 … d−m)r = Σ di · ri,  i = −m থেকে n−1,  0 ≤ di ≤ r−1

এই একটি সূত্র থেকেই সব আসে। r = ১০ হলে আমাদের চেনা দশমিক। r = ২ হলে binary — অঙ্ক শুধু 0 আর 1, যাকে বলে bit (binary digit). r = ৮ হলে octal, অঙ্ক 0–7. r = ১৬ হলে hexadecimal বা সংক্ষেপে hex — এখানে ১০টি অঙ্কে কুলায় না, তাই ১০ থেকে ১৫-এর জন্য A, B, C, D, E, F ধার করা হয়।

সবচেয়ে ডানের বিটের ওজন সবচেয়ে কম — LSB (least significant bit); সবচেয়ে বাঁয়ের বিট MSB (most significant bit). বিন্দুর ডানে ঋণাত্মক ঘাত: (0.101)2 = ১/২ + ১/৮ = ০.৬২৫।

দুটি সহজ কিন্তু জরুরি সত্য

(১) ভিত্তি নিজেই সবসময় “10”. যেকোনো ভিত্তিতে r লিখলে হয় (10)r — binary-তে দুই হলো 10, hex-এ ষোলো হলো 10. তাই “10” দেখে মান বোঝা যায় না, ভিত্তি লিখতেই হয়: (10)2, 0x10, 010 — তিনটির মান আলাদা।
(২) n অঙ্কের সবচেয়ে বড় সংখ্যা rn − 1. তিন অঙ্কের দশমিকে ৯৯৯ = ১০৩ − ১, আর ৮ বিটে 11111111 = ২৮ − ১ = ২৫৫. কারণ (r−1)(rn−1 + … + r + 1) একটি গুণোত্তর ধারা, যার যোগফল rn − 1.

কোড আর বইয়ে ভিত্তি বোঝানোর কয়েকটি রীতি আছে। গণিতে নিচে ছোট করে লেখা হয়: (9C)16. C, JavaScript, Python-এ hex লেখা হয় 0x9C, binary 0b10011100. C-তে শুরুতে একটি 0 দিলে octal — 0234 মানে দশমিক ১৫৬, এটি একটি পুরোনো ফাঁদ যা নিচে কোড চালিয়ে দেখবেন। Assembly-তে অনেক সময় শেষে h লেখা হয় (9Ch), আর Unicode-এ U+0985.

২ · কেন binary, আর octal-hex কেন তার সংক্ষিপ্ত রূপ

মানুষ দশ দিয়ে গোনে কারণ আমাদের দশটি আঙুল। কম্পিউটার দুই দিয়ে গোনে, কারণ তার “আঙুল” হলো transistor — আর একটি transistor-কে নির্ভরযোগ্যভাবে দুটি অবস্থায় রাখা সবচেয়ে সহজ: চালু অথবা বন্ধ, উঁচু ভোল্টেজ অথবা নিচু ভোল্টেজ। ঘরের সুইচবোর্ডের সুইচের মতো — মাঝামাঝি কোনো অবস্থা নেই, তাই ভুল বোঝারও সুযোগ কম।

কিন্তু আসল কারণ আরও গভীর: শব্দ বা noise. তারের ভোল্টেজ কখনো ঠিক নিখুঁত থাকে না — পাশের তারের প্রভাব, তাপমাত্রা, বিদ্যুতের ওঠানামা সব মিলিয়ে একটু কাঁপে। দুটি মাত্র স্তর থাকলে মাঝখানে বড় একটি নিরাপদ ফাঁক রাখা যায়। প্রচলিত 5 V TTL লজিকে যেমন: পাঠানো gate “1” বোঝাতে কমপক্ষে ২.৪ V দেয়, আর গ্রহণকারী gate ২.০ V-এর ওপরে পেলেই 1 ধরে নেয়। মাঝের ০.৪ V-এর এই ছাড়টিকে বলে noise margin — এর চেয়ে কম কাঁপুনিতে উত্তর বদলায় না। “0”-এর দিকেও একই রকম: পাঠানো ০.৪ V-এর নিচে, গ্রহণ ০.৮ V পর্যন্ত।

রেলস্টেশনের পুরোনো সিগন্যালের কথা ভাবুন। হাতল হয় ওপরে, নয় নিচে — দূর থেকে কুয়াশার মধ্যেও চালক ভুল বোঝেন না। এখন যদি দশটি ভিন্ন কোণে হাতল রেখে দশটি আলাদা বার্তা দেওয়ার চেষ্টা করা হতো, একটু বাতাসেই ৬ আর ৭ গুলিয়ে যেত। দশটি স্তরের ভোল্টেজও তেমনি — প্রতিটি স্তরের মাঝের ফাঁক ছোট হয়ে যায়, আর সামান্য noise-এই ভুল মান পড়া হয়।

আরও একটি সুবিধা: দুই অবস্থার জগতে লজিক আর হিসাব একই ভাষায় চলে — সত্য/মিথ্যা আর 1/0 একই জিনিস। CMOS transistor কীভাবে এই দুই স্তর তৈরি করে, আর noise margin ও delay কোথা থেকে আসে, তা দেখবেন পর্ব ০৮-এ (Logic gate আর transistor).

Binary লম্বা, তাই octal আর hex

Binary-র একটি অসুবিধা আছে — খুব লম্বা। দশমিক ২৯২৫ লিখতে লাগে ১২টি বিট: 1011 0110 1101. মানুষের চোখে এমন সারি পড়া, মুখে বলা বা খাতায় কপি করা খুব ভুলপ্রবণ। সমাধান হলো এমন একটি ভিত্তি নেওয়া যার মান ২-এর একটি ঘাত। ৮ = ২৩, তাই একটি octal অঙ্ক ঠিক ৩টি বিট। ১৬ = ২৪, তাই একটি hex অঙ্ক ঠিক ৪টি বিট — যাকে বলে একটি nibble, আর দুটি hex অঙ্কে একটি পুরো byte (৮ বিট)।

দশমিকbinary (৪ বিট)octalhexদশমিকbinary (৪ বিট)octalhex
০000000৮1000108
১000111৯1001119
২001022১০101012A
৩001133১১101113B
৪010044১২110014C
৫010155১৩110115D
৬011066১৪111016E
৭011177১৫111117F

এই ১৬টি সারি মুখস্থ থাকলে binary ↔ hex রূপান্তর হয় চোখের পলকে। কেন দল বানানো কাজ করে, তার প্রমাণ অংশ ৬-এ দেখব। এখন শুধু মনে রাখুন: octal আর hex নতুন কোনো সংখ্যা নয় — একই bit-গুলোকে মানুষের পড়ার মতো করে লেখা। CPU ভেতরে সবসময় binary-ই রাখে।

৩ · ভিত্তি r থেকে দশমিক — Horner-এর পদ্ধতি

সবচেয়ে সরল রাস্তা হলো সূত্রে সরাসরি বসানো। (2A7)16 = ২·১৬২ + ১০·১৬ + ৭ = ৫১২ + ১৬০ + ৭ = ৬৭৯। কাজ চলে, কিন্তু প্রতিটি ঘরের জন্য আলাদা করে ঘাত হিসাব করতে হয় — অঙ্ক বেশি হলে গুণ অনেক বেশি।

চতুর একটি বিকল্প হলো Horner-এর পদ্ধতি। বহুপদীটিকে ভেতর থেকে বাইরে সাজানো যায়:

d3r3 + d2r2 + d1r + d0 = ((d3·r + d2)·r + d1)·r + d0

অর্থাৎ বাঁ দিক থেকে একটি একটি অঙ্ক পড়ুন, আর প্রতিবার v ← v·r + d. কোনো ঘাত আলাদা করে হিসাব করতে হয় না; n অঙ্কের জন্য মাত্র n−1টি গুণ। এটি বহুপদীর মান বের করার সবচেয়ে কম গুণের সাধারণ পদ্ধতি হিসেবে পরিচিত।

(2A7)16

v = 2
v = 2·16 + 10 = 42
v = 42·16 + 7 = 679

(4231)5

v = 4
v = 4·5 + 2 = 22
v = 22·5 + 3 = 113
v = 113·5 + 1 = 566

(10011100)2

1 → 2 → 4 → 9 → 19 → 39 → 78 → 156
প্রতিবার দ্বিগুণ, আর অঙ্ক 1 হলে আরও এক যোগ।

শেষ উদাহরণটি খেয়াল করুন: সংখ্যাগুলো 1, 2, 4, 9, 19, 39, 78, 156 — এগুলো ঠিক সেই ভাগফলগুলো, যা পরের অংশে ১৫৬-কে বারবার ২ দিয়ে ভাগ করলে পাবেন, শুধু উল্টো ক্রমে। Horner আর বারবার ভাগ একই সিঁড়ির ওঠা আর নামা।

ভগ্নাংশ অংশ: বিন্দুর ডানের অঙ্কগুলোর জন্যও Horner চলে, শুধু ডান দিক থেকে আর ভাগ দিয়ে: (0.d−1d−2d−3)r = (d−1 + (d−2 + d−3/r)/r)/r. ছোট উদাহরণে সরাসরি যোগ করাই সহজ: (1011.101)2 = ৮ + ২ + ১ + ০.৫ + ০.১২৫ = ১১.৬২৫, আর (2A7.C)16 = ৬৭৯ + ১২/১৬ = ৬৭৯.৭৫।

প্রতিদিনের কোডেও Horner লুকিয়ে আছে: লেখা "566"-কে সংখ্যা বানাতে strtol বা যেকোনো parser (লেখা পড়ে মান বের করার কোড) করে v = v * 10 + (c - '0'). ভিত্তি ২ হলে hardware-এর কাছে “×২” মানে শুধু এক ঘর বাঁয়ে সরানো (shift) — সবচেয়ে সস্তা কাজ।

৪ · দশমিক পূর্ণসংখ্যা থেকে ভিত্তি r — বারবার ভাগ আর তার প্রমাণ

উল্টো দিকের নিয়মটি সবার জানা: সংখ্যাটিকে r দিয়ে ভাগ করতে থাকুন যতক্ষণ না ভাগফল ০ হয়, তারপর ভাগশেষগুলো উল্টো ক্রমে (শেষটি আগে) পড়ুন। এই পদ্ধতির নাম repeated division. কিন্তু কেন উল্টো? আর কেন ভাগশেষই অঙ্ক?

প্রমাণ — এক লাইনের মূল যুক্তি

ধরুন N = (dk … d1 d0)r. সূত্রটি এভাবে লেখা যায়:

N = r · (dk rk−1 + … + d1) + d0,   যেখানে 0 ≤ d0 < r

ভাগের নিয়ম (division algorithm) বলে, N-কে r দিয়ে ভাগ করলে ভাগফল q আর ভাগশেষ রাখার উপায় একটিই, যেখানে 0 ≤ ভাগশেষ < r. ওপরের সমীকরণ ঠিক সেই রূপে আছে। তাই ভাগশেষ = d0 (সবচেয়ে ডানের অঙ্ক), আর ভাগফল q = (dk … d1)r — অর্থাৎ শেষ অঙ্কটি ফেলে দেওয়া একই সংখ্যা। এবার q-কে আবার ভাগ করলে পাওয়া যায় d1, এভাবে চলতে থাকে (গাণিতিক আরোহ বা induction). প্রতিটি ধাপে ভাগফল কমে, তাই একসময় ০ হবেই। আর প্রথম ভাগশেষটি LSB বলে উত্তর পড়তে হয় নিচ থেকে ওপরে।

Binary-র ক্ষেত্রে ব্যাপারটি চোখে দেখা যায়: ২ দিয়ে ভাগ মানে binary লেখার শেষ বিটটি কেটে ফেলা — যেমন দশমিকে ১০ দিয়ে ভাগ করলে শেষ অঙ্ক কেটে যায় (১৫৬ ÷ ১০ = ১৫, ভাগশেষ ৬)। নিচের stepper-এ প্রতিটি ধাপে একটি অপরিবর্তনীয় সম্পর্ক (invariant) মিলিয়ে দেখানো হয়েছে: ১৫৬ = q · ২k + (এ পর্যন্ত পাওয়া বিটগুলোর মান). এটি প্রতিটি ধাপে সত্য থাকে, আর শেষে q = ০ হলে বাকি থাকে শুধু উত্তরটি।

হিসাবের খাতা
এই ধাপের ভাগ
বাকি ভাগফল q
পাওয়া বিট (ডান থেকে জমছে)
k (কয়টি বিট)
invariant: q·2^k + বিটের মান
  1. শুরু। q = ১৫৬, এখনো কোনো বিট পাইনি (k = ০)। Invariant তুচ্ছভাবে সত্য: ১৫৬ · ১ + ০ = ১৫৬। লক্ষ্য: q-কে ০-তে নামানো, আর সেই পথে প্রতিটি ভাগশেষ জমানো।
  2. 156 ÷ 2 = 78, ভাগশেষ 0. ১৫৬ জোড়, তাই LSB হলো 0. এখন ১৫৬ = ৭৮ · ২ + ০ — প্রথম বিটটি “আলাদা করে রাখা” হলো, বাকি সব বিট আছে ৭৮-এর ভেতরে।
  3. 78 ÷ 2 = 39, ভাগশেষ 0. দ্বিতীয় বিটও 0, জমেছে 00. Invariant: ৩৯ · ৪ + ০ = ১৫৬ — কারণ q-এর প্রতিটি একক এখন ২২ = ৪-এর সমান।
  4. 39 ÷ 2 = 19, ভাগশেষ 1. নতুন বিট 1 জমেছে বাঁ দিকে: 100, যার মান ৪। Invariant: ১৯ · ৮ + ৪ = ১৫২ + ৪ = ১৫৬ — মিলে গেছে।
  5. 19 ÷ 2 = 9, ভাগশেষ 1. জমেছে 1100 = ১২। Invariant: ৯ · ১৬ + ১২ = ১৪৪ + ১২ = ১৫৬।
  6. 9 ÷ 2 = 4, ভাগশেষ 1. জমেছে 11100 = ২৮। Invariant: ৪ · ৩২ + ২৮ = ১২৮ + ২৮ = ১৫৬।
  7. 4 ÷ 2 = 2, ভাগশেষ 0. নতুন বিট 0, তাই জমা বিটের মান ২৮-ই থাকে, শুধু দৈর্ঘ্য বাড়ে: 011100. Invariant: ২ · ৬৪ + ২৮ = ১৫৬।
  8. 2 ÷ 2 = 1, ভাগশেষ 0. জমেছে 0011100. q এখনো ১ — এখানে থামলে MSB হারিয়ে যেত, যা খাতায় খুব সাধারণ ভুল। Invariant: ১ · ১২৮ + ২৮ = ১৫৬।
  9. 1 ÷ 2 = 0, ভাগশেষ 1. শেষ ভাগশেষ 1 হলো MSB. এখন q = ০, তাই থামুন। জমা বিট 10011100, আর invariant বলছে ০ · ২৫৬ + ১৫৬ = ১৫৬ — পুরো মানটি এখন বিটগুলোর ভেতরে।
  10. উত্তর ও যাচাই। (156)10 = (10011100)2. যাচাই: ১২৮ + ১৬ + ৮ + ৪ = ১৫৬। Invariant-টি প্রতিটি ধাপে সত্য ছিল, আর ধাপের সংখ্যা সসীম — এটাই পুরো পদ্ধতির প্রমাণ।

ভিত্তি বদলালে শুধু ভাজক বদলায়। ১৫৬-কে ৮ দিয়ে ভাগ করলে ভাগশেষ ৪, ৩, ২ — উত্তর (234)8. ১৬ দিয়ে ভাগ করলে ১২ (= C) আর ৯ — উত্তর (9C)16. আর ৫৬৬-কে ৫ দিয়ে ভাগ করলে ভাগশেষ ১, ৩, ২, ৪ — উত্তর (4231)5, যা অংশ ৩-এর Horner-এর হিসাবের সাথে মিলে যায়।

দ্রুত বিকল্প — ২-এর ঘাত বাদ দেওয়া: ছোট সংখ্যায় ভাগের বদলে সবচেয়ে বড় যে ২-এর ঘাত ঢোকে সেটি বাদ দিন। ১৫৬ − ১২৮ = ২৮, ২৮ − ১৬ = ১২, ১২ − ৮ = ৪, ৪ − ৪ = ০। অর্থাৎ ১২৮, ১৬, ৮ আর ৪-এর ঘরে 1, বাকিগুলোতে 0. এটি মূলত একটি greedy পদ্ধতি (প্রতি ধাপে সবচেয়ে বড়টি বেছে নেওয়া) — আর এখানে সবসময় ঠিক উত্তর দেয়, কারণ ২k > ২k−1 + … + ১।

৫ · দশমিক ভগ্নাংশ — বারবার গুণ, আর যে ভগ্নাংশ কখনো শেষ হয় না

ভগ্নাংশের জন্য নিয়মটি উল্টো: ভগ্নাংশটিকে r দিয়ে গুণ করুন। গুণফলের পূর্ণ অংশই পরের অঙ্ক; পূর্ণ অংশ ফেলে বাকি ভগ্নাংশ নিয়ে আবার গুণ করুন। ভগ্নাংশ ০ হলে, বা যত ঘর দরকার তত ঘর পেলে থামুন। এবার অঙ্কগুলো পড়তে হয় ওপর থেকে নিচে — প্রথমটিই বিন্দুর ঠিক পরের অঙ্ক। এর নাম repeated multiplication.

প্রমাণ: ধরুন F = (0.d−1d−2…)r = d−1/r + d−2/r2 + … তাহলে r · F = d−1 + (d−2/r + d−3/r2 + …)। বন্ধনীর ভেতরের অংশটি (0.d−2d−3…)r, যা ১-এর চেয়ে ছোট। তাই r·F-এর পূর্ণ অংশ ঠিক d−1, আর ভগ্নাংশ অংশটি বাকি অঙ্কগুলোর সংখ্যা। পূর্ণসংখ্যার ভাগ যেমন ডান দিক থেকে অঙ্ক “কেটে” আনে, গুণ তেমনি বিন্দুর ডান দিক থেকে অঙ্ক বাঁয়ে “টেনে” পূর্ণ অংশে নিয়ে আসে।

হিসাবের খাতা
বর্তমান ভগ্নাংশ f
f × 2
পূর্ণ অংশ = পরের বিট
জমা উত্তর (বাঁ থেকে ডানে)
যাচাই
  1. শুরু। f = ০.৬২৫। প্রতিবার ২ দিয়ে গুণ করব; পূর্ণ অংশ (0 বা 1) হবে পরের বিট, আর অঙ্ক জমবে বিন্দুর পরে বাঁ থেকে ডানে।
  2. 0.625 × 2 = 1.25. পূর্ণ অংশ 1 — বিন্দুর পরের প্রথম বিট (ওজন ১/২)। বাকি ভগ্নাংশ ০.২৫। যাচাই: ০.৫ + ০.২৫/২ = ০.৬২৫ — বাকি অংশের ওজন এখন অর্ধেক।
  3. 0.25 × 2 = 0.5. পূর্ণ অংশ 0, তাই দ্বিতীয় বিট 0 (ওজন ১/৪-এর ঘরে কিছু নেই)। বাকি ০.৫। জমেছে 0.10.
  4. 0.5 × 2 = 1.0. পূর্ণ অংশ 1, আর বাকি ভগ্নাংশ ঠিক ০ — থামুন। জমেছে 0.101. যাচাই: ০.৫ + ০.১২৫ = ০.৬২৫।
  5. উত্তর। (0.625)10 = (0.101)2. তিন ধাপেই শেষ হলো, কারণ ০.৬২৫ = ৫/৮, আর হর ৮ = ২৩ — ২-এর একটি ঘাত। এমন ভগ্নাংশ binary-তে সবসময় সসীম।
  6. এবার 0.1. একই নিয়মে: ০.১ → ০.২ (0), ০.৪ (0), ০.৮ (0), ১.৬ (1, বাকি ০.৬), ১.২ (1, বাকি ০.২)… f আবার ০.২-তে ফিরে এসেছে! এরপর একই চার বিট 0011 চিরকাল ঘুরবে। (0.1)10 = (0.0 0011 0011 …)2 — কখনো শেষ হয় না।

কোন ভগ্নাংশ শেষ হয়, কোনটি হয় না — একটি পূর্ণ নিয়ম

৬ নম্বর ধাপের চক্রটি কাকতালীয় নয়। একটি ভগ্নাংশকে লঘিষ্ঠ আকারে p/q লিখুন (p আর q-এর মধ্যে কোনো সাধারণ উৎপাদক নেই)। তাহলে:

সসীমতার নিয়ম

ভিত্তি r-এ p/q-এর লেখা সসীম হবে যদি এবং কেবল যদি q-এর প্রতিটি মৌলিক উৎপাদক r-কেও ভাগ করে।
যুক্তি: k ঘরে শেষ হওয়া মানে p/q = m/rk কোনো পূর্ণসংখ্যা m-এর জন্য, অর্থাৎ q ভাগ করে rk-কে। উল্টো দিকে, q | rk হলে p/q = (p·rk/q)/rk — k ঘরেই শেষ। আর সসীম না হলে লেখাটি অবশ্যই পর্যাবৃত্ত (চক্রাকার), কারণ বারবার গুণের পর ভাগশেষ মাত্র q−1 রকম হতে পারে — কোনো একটি আবার আসবেই।

০.১ = ১/১০, আর ১০ = ২ × ৫। মৌলিক উৎপাদক ৫ ভিত্তি ২-কে ভাগ করে না — তাই binary-তে অসীম। কিন্তু দশমিকে ১০-কে ২ আর ৫ দুটোই ভাগ করে, তাই ১/২, ১/৪, ১/৫, ১/৮ সব দশমিকে সসীম। উল্টো দিকে ১/৩ দশমিকে অসীম (০.৩৩৩…), কিন্তু ভিত্তি ৩-এ মাত্র (0.1)3. কোনো ভিত্তিই “নিখুঁত” নয় — প্রতিটি ভিত্তি কিছু ভগ্নাংশ সসীমভাবে লিখতে পারে, কিছু পারে না।

এখান থেকেই floating point-এর “অদ্ভুত” ফল। কম্পিউটার সাধারণত ভগ্নাংশ রাখে নির্দিষ্ট সংখ্যক binary অঙ্কে (floating point, ৬৪-বিটের double-এ ৫৩ বিট নির্ভুলতা)। অসীম 0.0001100110011… কোথাও কেটে গোল করতেই হয়, তাই জমা হওয়া মানটি ০.১-এর খুব কাছাকাছি কিন্তু ঠিক ০.১ নয়। এজন্যই বহু ভাষায় 0.1 + 0.2 == 0.3 মিথ্যা হয়। IEEE 754 কীভাবে এই বিটগুলো সাজায় আর কীভাবে গোল করে, তা দেখবেন পর্ব ২৩ (Floating point) আর পর্ব ২৪ (Floating point-এর হিসাব)-এ। টাকা-পয়সার হিসাবে তাই সাধারণত পয়সাকে পূর্ণসংখ্যা হিসেবে রাখা হয়, বা দশমিক ধরনের সংখ্যা ব্যবহার করা হয়।

হাতের প্রতিটি পদ্ধতি এবার যন্ত্রে মিলিয়ে নিন। নিচের converter পূর্ণসংখ্যার প্রতিটি ভাগ আর ভগ্নাংশের প্রতিটি গুণ ছাপে, তারপর Horner দিয়ে উল্টো যাচাই করে। একটি সূক্ষ্ম বিষয়: ভগ্নাংশকে এখানে JavaScript-এর সাধারণ দশমিক সংখ্যা হিসেবে না রেখে দুটি পূর্ণসংখ্যার ভাগ (num/den) হিসেবে রাখা হয়েছে — কারণ JavaScript-এর সংখ্যা নিজেই binary floating point, আর তাতে ০.১ আগে থেকেই একটু ভুল। নিজের সংখ্যা দিয়ে বদলে চালিয়ে দেখুন — Run চাপুন:

// Universal base converter: every division / multiplication step is printed.
const DIG = "0123456789ABCDEF";
function intToBase(n, r) {                 // repeated division
  let out = "", steps = [];
  while (n > 0) {
    const q = Math.floor(n / r), d = n % r;
    steps.push(`${n}/${r}=${q} r${DIG[d]}`);
    out = DIG[d] + out; n = q;             // remainders read in reverse
  }
  console.log("   " + steps.join(", "));
  return out || "0";
}
function fracToBase(num, den, r, places, show) {   // exact fraction num/den
  let out = "";
  for (let i = 0; i < places && num !== 0; i++) {
    const p = num * r, d = Math.floor(p / den);
    if (i < show) console.log(`   ${num / den} x ${r} = ${p / den} -> digit ${DIG[d]}`);
    out += DIG[d]; num = p % den;          // keep only the fraction part
  }
  return "0." + out + (num !== 0 ? "..." : "");
}
function fromBase(s, r) {                  // Horner: v = v*r + digit
  let v = 0;
  for (const c of s) v = v * r + DIG.indexOf(c);
  return v;
}
for (const [n, r] of [[156, 2], [156, 8], [156, 16], [566, 5]]) {
  console.log(`${n} (base 10) -> base ${r}`);
  console.log(`   result: ${intToBase(n, r)}`);
}
console.log("0.625 -> base 2");  console.log("   result:", fracToBase(5, 8, 2, 10, 10));
console.log("0.1 -> base 2 (first 5 steps)");  console.log("   20 places:", fracToBase(1, 10, 2, 20, 5));
console.log("0.1 -> base 16:", fracToBase(1, 10, 16, 8, 0));
console.log("Horner check: 10011100_2 =", fromBase("10011100", 2), "| 2A7_16 =", fromBase("2A7", 16), "| 4231_5 =", fromBase("4231", 5));
console.log("4231_5 -> base 7 via decimal:");  console.log("   result:", intToBase(fromBase("4231", 5), 7));

আউটপুট (নির্ধারিত — প্রতিবার একই):

156 (base 10) -> base 2
   156/2=78 r0, 78/2=39 r0, 39/2=19 r1, 19/2=9 r1, 9/2=4 r1, 4/2=2 r0, 2/2=1 r0, 1/2=0 r1
   result: 10011100
156 (base 10) -> base 8
   156/8=19 r4, 19/8=2 r3, 2/8=0 r2
   result: 234
156 (base 10) -> base 16
   156/16=9 rC, 9/16=0 r9
   result: 9C
566 (base 10) -> base 5
   566/5=113 r1, 113/5=22 r3, 22/5=4 r2, 4/5=0 r4
   result: 4231
0.625 -> base 2
   0.625 x 2 = 1.25 -> digit 1
   0.25 x 2 = 0.5 -> digit 0
   0.5 x 2 = 1 -> digit 1
   result: 0.101
0.1 -> base 2 (first 5 steps)
   0.1 x 2 = 0.2 -> digit 0
   0.2 x 2 = 0.4 -> digit 0
   0.4 x 2 = 0.8 -> digit 0
   0.8 x 2 = 1.6 -> digit 1
   0.6 x 2 = 1.2 -> digit 1
   20 places: 0.00011001100110011001...
0.1 -> base 16: 0.19999999...
Horner check: 10011100_2 = 156 | 2A7_16 = 679 | 4231_5 = 566
4231_5 -> base 7 via decimal:
   566/7=80 r6, 80/7=11 r3, 11/7=1 r4, 1/7=0 r1
   result: 1436

প্রথম চারটি রূপান্তর stepper আর হাতের হিসাবের সাথে হুবহু মেলে। ০.১-এর ২০ ঘর 0.00011001100110011001... — প্রথম 0-এর পর 0011 বারবার ঘুরছে, আর শেষের ... বলছে ভগ্নাংশ তখনো ০ হয়নি। Hex-এ একই ০.১ হলো 0.1999… — 9 মানে binary 1001, অর্থাৎ একই চক্র ৪ বিটের দলে দেখা। এ কারণেই double-এ ০.১-এর বিট-নকশা hex-এ দেখলে …999A ধরনের লেজ দেখা যায় (শেষে গোল করার ফলে A) — বিস্তারিত পর্ব ২৩-এ। শেষ লাইনে (4231)5 → (1436)7 — দুই অপরিচিত ভিত্তির মাঝে দশমিক সেতু, যা পরের অংশে দেখব।

৬ · Binary ↔ octal ↔ hex — দল বানানো, আর যেকোনো ভিত্তি থেকে যেকোনো ভিত্তি

Binary থেকে hex করতে ভাগ বা গুণ লাগে না — শুধু বিটগুলোকে বিন্দু থেকে শুরু করে ৪টি করে দলে ভাগ করুন, আর প্রতিটি দলকে একটি hex অঙ্কে বদলান। Octal-এর জন্য দল ৩টি করে। কেন কাজ করে?

প্রমাণ: binary সংখ্যার বিটগুলোকে ডান থেকে ৪টি করে দলে নিন: N = Σ bi·২i = Σj (b4j+3·৮ + b4j+2·৪ + b4j+1·২ + b4j) · ২4j. বন্ধনীর ভেতরের মান hj সবসময় ০ থেকে ১৫-এর মধ্যে, আর ২4j = ১৬j. তাই N = Σ hj·১৬j — ঠিক একটি hex সংখ্যার সূত্র, যার অঙ্কগুলো hj. ভগ্নাংশ অংশে একই যুক্তি ২−4j = ১৬−j দিয়ে চলে। Octal-এর জন্য ৪-এর জায়গায় ৩। প্রতিটি দল নিজে নিজে রূপান্তর হয়, এক দল থেকে অন্য দলে কোনো carry যায় না — এটাই এর গতির রহস্য।

শুরু১২টি বিট
bits:  1 0 1 1 0 1 1 0 1 1 0 1
       ↑ MSB               LSB ↑
hex-এর জন্যডান থেকে ৪টি করে
       1011 │ 0110 │ 1101
weight  8421 │ 8421 │ 8421
প্রতিটি দল → একটি অঙ্ক8421 ওজন যোগ
       1011 │ 0110 │ 1101
       8+2+1│  4+2 │8+4+1
        =11 │   =6 │  =13
         B  │   6  │   D     ->  0xB6D
octal-এর জন্যডান থেকে ৩টি করে
       101 │ 101 │ 101 │ 101
weight  421 │ 421 │ 421 │ 421
প্রতিটি দল → একটি অঙ্ক421 ওজন যোগ
       101 │ 101 │ 101 │ 101
       4+1 │ 4+1 │ 4+1 │ 4+1
        5  │  5  │  5  │  5     ->  (5555)8
যাচাইদুই পথেই দশমিক
0xB6D   = 11·256 + 6·16 + 13   = 2816 + 96 + 13  = 2925
(5555)8 = 5·512 + 5·64 + 5·8 + 5 = 2560 + 320 + 40 + 5 = 2925
  1. শুরু। ১২টি বিট: 101101101101. এটি একটি পূর্ণসংখ্যা, তাই “বিন্দু” আছে একদম ডানে — দল বানানো শুরু হবে LSB থেকে।
  2. Hex: ডান থেকে ৪টি করে দল। পাওয়া গেল 1011, 0110, 1101. ১২ বিট ৪ দিয়ে বিভাজ্য, তাই বাঁয়ে কোনো 0 বসাতে হলো না। প্রতিটি দলের ভেতরে ওজন 8, 4, 2, 1.
  3. প্রতিটি দল একটি hex অঙ্ক। 1011 = ৮ + ২ + ১ = ১১ = B; 0110 = ৪ + ২ = ৬; 1101 = ৮ + ৪ + ১ = ১৩ = D. উত্তর 0xB6D. প্রতিটি দল আলাদাভাবে হলো, কোনো carry লাগেনি।
  4. Octal: ডান থেকে ৩টি করে দল। একই বিট, এবার 101, 101, 101, 101. ১২ বিট ৩ দিয়েও বিভাজ্য, তাই এখানেও padding লাগল না। ওজন 4, 2, 1.
  5. প্রতিটি দল একটি octal অঙ্ক। 101 = ৪ + ১ = ৫, চারবার। উত্তর (5555)8. একই বিট-সারি, শুধু দলের মাপ আলাদা — তাই hex আর octal অঙ্কের মধ্যে সরাসরি কোনো মিল চোখে পড়ে না।
  6. যাচাই। Hex পথে ১১·২৫৬ + ৬·১৬ + ১৩ = ২৯২৫, octal পথে ৫·৫১২ + ৫·৬৪ + ৫·৮ + ৫ = ২৯২৫। দুটিই একই দশমিক মান — অর্থাৎ (1011 0110 1101)2 = 0xB6D = (5555)8 = ২৯২৫।

ভগ্নাংশ আর padding — যেখানে সবাই ভুল করেন

বিন্দু থাকলে দল বানাতে হয় বিন্দু থেকে দুই দিকে: পূর্ণ অংশে বিন্দু থেকে বাঁয়ে, ভগ্নাংশে বিন্দু থেকে ডানে। অসম্পূর্ণ দলে 0 বসাতে হয় — পূর্ণ অংশে বাঁ প্রান্তে, ভগ্নাংশে ডান প্রান্তে। উদাহরণ (10110.1011)2:

  • Hex: পূর্ণ অংশ 1 0110 → 0001 0110 = 16; ভগ্নাংশ 1011 = B. উত্তর (16.B)16.
  • Octal: পূর্ণ অংশ 10 110 → 010 110 = 26; ভগ্নাংশ 101 1 → 101 100 = 54. উত্তর (26.54)8.
  • যাচাই: তিনটিই ২২.৬৮৭৫ — ২২ + ১১/১৬ = ২২ + ৫/৮ + ৪/৬৪।

ভগ্নাংশের শেষ দলে 0 বাঁয়ে বসালে (001 = 1) উত্তর হতো ২৬.৫১ — সম্পূর্ণ ভুল। কারণ ভগ্নাংশের ডানে 0 বসালে মান বদলায় না (০.১০ = ০.১), কিন্তু বাঁয়ে বসালে বদলায়।

যেকোনো ভিত্তি a থেকে যেকোনো ভিত্তি b

দুটি পথ আছে। সাধারণ পথ — দশমিকের সেতু: প্রথমে Horner দিয়ে a → দশমিক, তারপর বারবার ভাগ দিয়ে দশমিক → b. যেমন (4231)5 = ৫৬৬, আর ৫৬৬-কে ৭ দিয়ে ভাগ করলে ভাগশেষ ৬, ৩, ৪, ১ — উত্তর (1436)7. (চাইলে সরাসরি ভিত্তি b-এর হিসাবেই Horner চালানো যায়, কিন্তু অপরিচিত ভিত্তিতে গুণ-যোগ মানুষের জন্য ভুলপ্রবণ।) শর্টকাট — দুটিই ২-এর ঘাত হলে binary-র সেতু: octal → hex করতে আগে প্রতিটি octal অঙ্ককে ৩ বিটে খুলুন, তারপর ৪ বিটে নতুন করে দল বানান। (725)8 → 111 010 101 → 0001 1101 0101 → (1D5)16; দুটিই দশমিক ৪৬৯। একই কৌশল ভিত্তি ৪ ↔ ১৬ বা ২ ↔ ৩২-এর জন্যও চলে, কারণ শর্ত শুধু একটাই: দুটি ভিত্তিই একই সংখ্যার ঘাত।

এবার একই কাজ C-তে দেখুন। printf নিজেই octal (%o) আর hex (%x, %X) ছাপতে পারে, কিন্তু binary-র জন্য standard কোনো format নেই (C23-এ %b যুক্ত হয়েছে, তবে সব library-তে এখনো নেই) — তাই একটি ছোট হাতে-লেখা ফাংশন। সাথে strtol, যা ভেতরে ঠিক Horner চালিয়ে ২ থেকে ৩৬ পর্যন্ত যেকোনো ভিত্তির লেখা পড়ে:

#include <stdio.h>
#include <stdlib.h>

static void print_bin(unsigned v, int width) {     /* MSB first, space every 4 */
    for (int i = width - 1; i >= 0; i--) {
        putchar(((v >> i) & 1u) ? '1' : '0');
        if (i % 4 == 0 && i != 0) putchar(' ');
    }
    putchar('\n');
}

int main(void) {
    unsigned nums[] = {156, 255, 2925};
    for (int i = 0; i < 3; i++) {
        printf("%4u  %%o=%-5o %%#x=%-#6x %%X=%-4X bin=", nums[i], nums[i], nums[i], nums[i]);
        print_bin(nums[i], 12);
    }
    printf("0xB6D=%d  0b10011100=%d  010=%d (leading 0 = octal!)\n", 0xB6D, 0b10011100, 010);
    printf("__STDC_VERSION__ = %ld\n", (long)__STDC_VERSION__);

    const char *txt[] = {"10011100", "234", "9C", "0x1F", "017"};
    int base[] = {2, 8, 16, 0, 0};                  /* base 0: detect 0x / 0 prefix */
    for (int i = 0; i < 5; i++)
        printf("strtol(\"%s\", base %d) = %ld\n", txt[i], base[i], strtol(txt[i], NULL, base[i]));
    return 0;
}

আউটপুট (gcc, Linux x86-64-এ চালানো):

 156  %o=234   %#x=0x9c   %X=9C   bin=0000 1001 1100
 255  %o=377   %#x=0xff   %X=FF   bin=0000 1111 1111
2925  %o=5555  %#x=0xb6d  %X=B6D  bin=1011 0110 1101
0xB6D=2925  0b10011100=156  010=8 (leading 0 = octal!)
__STDC_VERSION__ = 202311
strtol("10011100", base 2) = 156
strtol("234", base 8) = 156
strtol("9C", base 16) = 156
strtol("0x1F", base 0) = 31
strtol("017", base 0) = 15

প্রথম তিন লাইনে একই সংখ্যা চার রূপে: %o octal, %#x hex-এর সাথে 0x চিহ্নসহ, %X বড় হাতের hex, আর হাতে-লেখা print_bin প্রতি ৪ বিটে ফাঁক রেখে binary — খেয়াল করুন, ২৯২৫-এর binary-র প্রতিটি ৪ বিটের দল ঠিক hex-এর B, 6, D. চতুর্থ লাইনে 010 ছাপল ৮ — শুরুর 0 মানে octal. পঞ্চম লাইনে __STDC_VERSION__ = 202311 বলছে এই gcc ডিফল্টভাবে C23 (gnu23) মানে compile করছে; C23-এ 0b literal মানক অংশ, তাই -Wall -Wextra-এ কোনো warning আসেনি। পুরোনো মানে (যেমন -std=c17) এটি বহুদিনের একটি GCC extension — চলে, তবে -pedantic দিলে warning দেয়। শেষ পাঁচ লাইনে strtol তিন ভিত্তি থেকে একই ১৫৬ পড়েছে, আর base 0 দিলে লেখার শুরু দেখে নিজেই বুঝে নেয়: 0x1F hex (৩১), 017 octal (১৫)।

Terminal-এ থাকলে bash নিজেও রূপান্তর জানে: $((ভিত্তি#সংখ্যা)) লিখলে যেকোনো ভিত্তি (২ থেকে ৬৪) থেকে দশমিক, আর printf দিয়ে দশমিক থেকে octal বা hex:

printf '%x\n' 255          # decimal -> hex
printf '%o\n' 493          # decimal -> octal
echo $((2#1011))           # binary  -> decimal
echo $((16#ff))            # hex     -> decimal
echo $((8#755)) $((5#4231))   # octal and base 5 -> decimal
printf '%d\n' 0x1F         # printf also reads 0x... as hex
if command -v bc >/dev/null; then echo "obase=2; 156" | bc; else echo "bc নেই"; fi

আউটপুট:

ff
755
11
255
493 566
31
bc নেই

২৫৫ → ff, ৪৯৩ → 755 (octal; chmod 755-এর সেই সংখ্যা), 2#1011 → ১১, 16#ff → ২৫৫, 8#755 → ৪৯৩, আর 5#4231 → ৫৬৬ — Horner-এর হিসাবের সাথে মিলে গেল। এই container-এ bc নামের calculator ইনস্টল করা নেই, তাই শেষ লাইনে বিকল্প বার্তা এসেছে; নিজের Linux-এ থাকলে সেখানে 10011100 আসবে।

৭ · কত বিট লাগে, ২-এর ঘাত, KB বনাম KiB — আর hex-এর বাস্তব জগৎ

n বিটে ২nটি মান

প্রতিটি বিট দুই রকম হতে পারে, আর বিটগুলো স্বাধীন — তাই n বিটে মোট ২ × ২ × … × ২ = ২n রকম নকশা। Unsigned পূর্ণসংখ্যা হিসেবে এগুলো ০ থেকে ২n − ১। উল্টো প্রশ্নটি পরীক্ষায় বেশি আসে: ০ থেকে N পর্যন্ত সব মান রাখতে কত বিট লাগবে? আমাদের দরকার N + ১টি আলাদা নকশা, তাই সবচেয়ে ছোট n যেন ২n ≥ N + ১:

n = ⌈log2(N + 1)⌉
  • N = ২৫৫: ২৮ = ২৫৬ ≥ ২৫৬, তাই ৮ বিট। N = ২৫৬: লাগে ২৫৭টি নকশা, ২৮ যথেষ্ট নয় — ৯ বিট। একটি সংখ্যা বাড়লেই একটি বিট বেশি!
  • N = ১০০০: ২৯ = ৫১২ < ১০০১ ≤ ১০২৪ = ২১০, তাই ১০ বিট।
  • ১১ অঙ্কের মোবাইল নম্বর (সর্বোচ্চ ৯৯,৯৯৯,৯৯৯,৯৯৯) পূর্ণসংখ্যা হিসেবে রাখতে: log2(১০১১) ≈ ৩৬.৫৪, তাই ৩৭ বিট — ৩২-বিটের int-এ আঁটবে না। সাধারণ নিয়ম: d অঙ্কের দশমিক সংখ্যায় লাগে প্রায় d × ৩.৩২ বিট, কারণ log2১০ ≈ ৩.৩২।
  • ধরুন ১৭ কোটি মানুষকে আলাদা পরিচয়-নম্বর দিতে হবে: ২২৭ ≈ ১৩.৪ কোটি যথেষ্ট নয়, ২২৮ ≈ ২৬.৮ কোটি যথেষ্ট — ২৮ বিট।
২nমানhexকোথায় দেখবেন
২৪১৬0x10একটি hex অঙ্কের সব মান (nibble)
২৮২৫৬0x100একটি byte; রঙের প্রতিটি চ্যানেল ০–২৫৫
২১০১,০২৪0x400১ KiB
২১৬৬৫,৫৩৬0x10000TCP/UDP port-এর সংখ্যা; Unicode-এর প্রথম “plane”
২২০১০,৪৮,৫৭৬0x100000১ MiB
২৩০প্রায় ১০৭ কোটি0x40000000১ GiB
২৩২প্রায় ৪২৯ কোটি0x100000000৩২-বিট address-এ সর্বোচ্চ ৪ GiB মেমরি

SI বনাম IEC — ৫০০ GB ডিস্ক কেন “৪৬৫ GB” দেখায়

নতুন ল্যাপটপ কিনে অনেকেই অবাক হন: বাক্সে লেখা ৫০০ GB, কিন্তু Windows দেখাচ্ছে প্রায় ৪৬৫ GB. কেউ কিছু চুরি করেনি — দুই পক্ষ দুটি আলাদা “কিলো” ব্যবহার করছে। SI prefix-এ kilo = ১০৩, mega = ১০৬, giga = ১০৯ — ডিস্ক নির্মাতারা এটাই মানেন। কিন্তু মেমরি address স্বাভাবিকভাবেই ২-এর ঘাতে বাড়ে, তাই কম্পিউটার জগতে বহুদিন “KB” বলতে ১০২৪ বোঝানো হতো। এই দ্ব্যর্থতা দূর করতে IEC ১৯৯৮ সালে আলাদা নাম দেয় — IEC binary prefix: kibi (KiB = ২১০), mebi (MiB = ২২০), gibi (GiB = ২৩০), tebi (TiB = ২৪০)।

হিসাব করে দেখি

৫০০ GB = ৫০০ × ১০৯ byte. একে GiB-তে নিতে ভাগ করুন ২৩০ = ১,০৭৩,৭৪১,৮২৪ দিয়ে: ৫০০,০০০,০০০,০০০ ÷ ১,০৭৩,৭৪১,৮২৪ ≈ ৪৬৫.৬৬ GiB. Windows এই সংখ্যাটিই দেখায়, শুধু এককের নাম লেখে “GB”. পার্থক্য প্রতি ধাপে বাড়ে: kilo-তে ২.৪%, giga-তে প্রায় ৭.৪%, tera-তে প্রায় ১০% — তাই ১ TB ডিস্ক দেখায় প্রায় ৯৩১ GiB. macOS-এর নতুন version-গুলো আবার দশমিক GB-তেই দেখায়, তাই একই ডিস্ক Mac-এ “৫০০ GB”. RAM-এর ক্ষেত্রে প্রায় সবাই “GB” বলতে আসলে GiB বোঝায় — ৮ GB RAM মানে ৮ × ২৩০ byte.

Hex চারপাশেই

একবার চিনলে hex সবখানে চোখে পড়বে — কারণ যেখানেই byte-এর হিসাব, সেখানেই প্রতি byte-এ দুটি hex অঙ্ক সবচেয়ে পরিষ্কার লেখা।

রঙের কোড

ওয়েবপেজে #FF8800 মানে লাল FF = ২৫৫, সবুজ 88 = ১৩৬, নীল 00 = ০ — একটি কমলা রং। প্রতিটি চ্যানেল একটি byte, তাই মোট ২২৪ ≈ ১.৬৮ কোটি রং।

মেমরির ঠিকানা

Debugger বা crash রিপোর্টে 0x7ffd5a3c ধরনের ঠিকানা। মহল্লার বাড়ির নম্বরের মতো — প্রতিটি byte-এর একটি নম্বর, আর hex-এ লিখলে কোন ঠিকানা ৪ KiB-এর page-এর শুরুতে (শেষ তিন hex অঙ্ক 000) তা এক নজরে বোঝা যায়।

MAC address

নেটওয়ার্ক কার্ডের ৪৮ বিটের পরিচয়, ছয়টি byte: যেমন 00:1A:2B:3C:4D:5E (কাল্পনিক উদাহরণ)। প্রথম তিন byte সাধারণত নির্মাতার পরিচয়।

Unicode — বাংলা অ

বাংলা “অ”-এর Unicode code point U+0985, অর্থাৎ দশমিক ২৪৩৭। বাংলা ব্লক শুরু হয় U+0980-এ। ফাইলে UTF-8-এ “অ” রাখা হয় তিন byte-এ: E0 A6 85 — কীভাবে, তা পর্ব ০৬-এ।

৮ · Binary আর hex-এ সরাসরি হিসাব — যোগ, বিয়োগ, shift দিয়ে গুণ

দশমিকে ফিরে না গিয়েও যেকোনো ভিত্তিতে হিসাব করা যায় — নিয়ম একই, শুধু “১০ হলে হাতে এক” বদলে হয় “r হলে হাতে এক”। এখানে আমরা শুধু ধনাত্মক (unsigned) সংখ্যা নিচ্ছি; ঋণাত্মক সংখ্যা কীভাবে রাখা হয় আর বিয়োগকে কীভাবে যোগে বদলানো হয়, সেটি পর্ব ০৫-এর বিষয়।

Binary যোগ — carry

নিয়ম মাত্র চারটি: ০ + ০ = ০, ০ + ১ = ১, ১ + ১ = ১০ (লিখুন 0, হাতে 1), আর ১ + ১ + ১ = ১১ (লিখুন 1, হাতে 1). হাতে রাখা অঙ্কটির নাম carry.

  carry   1 1 1 1
            1 0 1 1      (11)
          + 0 1 1 1      ( 7)
          ---------
          1 0 0 1 0      (18)

ডান থেকে: ১ + ১ = ১০ → 0, carry 1; ১ + ১ + ১ = ১১ → 1, carry 1; ০ + ১ + ১ = ১০ → 0, carry 1; ১ + ০ + ১ = ১০ → 0, carry 1; শেষ carry নিজেই একটি নতুন বিট। খেয়াল করুন, দুটি ৪ বিটের সংখ্যা যোগ করে পেলাম ৫ বিট। যদি যন্ত্রের register মাত্র ৪ বিটের হয়, শেষ carry-টি জায়গা পায় না — ফল দেখাবে 0010 = ২। এটিই unsigned overflow, যা x86 বা ARM-এর মতো CPU একটি flag-এ জানিয়ে দেয়; RISC-V-এ flag নেই (পর্ব ০৫ ও পর্ব ২০)। প্রতিটি ঘরের এই “তিনটি বিট ঢোকে, যোগফল আর carry বের হয়” কাজটিই full adder circuit, যা পর্ব ১১-এ বানাবেন।

Binary বিয়োগ — borrow

বড় থেকে ছোট বিয়োগ (unsigned): ০ − ১ করতে হলে বাঁয়ের ঘর থেকে একটি borrow নিতে হয়, যা এই ঘরে ২-এর সমান। দশমিকে যেমন ধার করা ১ পাশের ঘরে ১০ হয়ে আসে।

            1 1 0 0      (12)
          − 0 1 0 1      ( 5)
          ---------
            0 1 1 1      ( 7)

ডান থেকে ঘর ০, ১, ২, ৩ ধরে: ঘর ০-এ ০ − ১ পারা যায় না, আর ঠিক বাঁয়ের ঘর ১-ও 0 — তাই ধার আসে ঘর ২-এর 1 থেকে। ঘর ২ হয় 0, ঘর ১ পায় ২, তা থেকে ১ পাঠায় ঘর ০-তে; ফলে ঘর ১-এ থাকে 1 আর ঘর ০ পায় ২। এখন ঘর ০: ২ − ১ = ১; ঘর ১: ১ − ০ = ১; ঘর ২: ০ − ১ — আবার ধার লাগে, এবার ঘর ৩-এর 1 থেকে: ২ − ১ = ১, আর ঘর ৩ হয় 0; ঘর ৩: ০ − ০ = ০। উত্তর 0111 = ৭। ছোট থেকে বড় বিয়োগ করতে গেলে সবচেয়ে বাঁয়ের ঘরেও ধার লাগে, কিন্তু ধার দেওয়ার মতো কোনো ঘর থাকে না — সেটাই ঋণাত্মক ফলের সংকেত, যা unsigned-এ রাখা যায় না।

Shift দিয়ে গুণ

দশমিকে ১০ দিয়ে গুণ মানে ডানে একটি ০ বসানো। একইভাবে binary-তে ২ দিয়ে গুণ মানে সব বিট এক ঘর বাঁয়ে সরানো (left shift): 1101 (১৩) → 11010 (২৬)। ২k দিয়ে গুণ মানে k ঘর shift — আর hex-এ ১৬ দিয়ে গুণ মানে একটি hex অঙ্ক সরানো, অর্থাৎ ৪ বিট: 0x2B × ১৬ = 0x2B0. অন্য সংখ্যা দিয়ে গুণ করতে তাকে ২-এর ঘাতে ভাঙুন: ১৩ × ১০ = ১৩ × (৮ + ২) = (১৩ ≪ ৩) + (১৩ ≪ ১) = ১০৪ + ২৬ = ১৩০। Compiler প্রায়ই ঠিক এভাবে ধ্রুবক দিয়ে গুণকে shift আর যোগে বদলে দেয়।

সাধারণ binary গুণও খাতার লম্বা গুণের মতো, শুধু প্রতিটি সারি হয় পুরো সংখ্যা (অঙ্ক 1) নয়তো ০ (অঙ্ক 0): 1011 × 101 = 1011 + 101100 = 110111, অর্থাৎ ১১ × ৫ = ৫৫। এই “shift আর যোগ” থেকেই hardware multiplier শুরু হয়, যা পর্ব ২১-এ Booth-এর পদ্ধতিসহ দেখবেন।

Hex যোগ — ১৬ হলে হাতে এক

  carry     1 1 1
            3 A 7 F      (14975)
          + 1 C 9 6      ( 7318)
          ---------
            5 7 1 5      (22293)

ডান থেকে: F + 6 = ১৫ + ৬ = ২১ = ১৬ + ৫ → লিখুন 5, carry 1. 7 + 9 + 1 = ১৭ = ১৬ + ১ → 1, carry 1. A + C + 1 = ১০ + ১২ + ১ = ২৩ = ১৬ + ৭ → 7, carry 1. 3 + 1 + 1 = ৫। উত্তর 0x5715, দশমিকে ১৪৯৭৫ + ৭৩১৮ = ২২২৯৩ — মিলে গেছে। কৌশল একটাই: প্রতিটি ঘরে দশমিকে যোগ করে ১৬ বা তার বেশি হলে ১৬ বাদ দিয়ে carry 1.

৯ · সংখ্যার কোড — BCD, excess-3 আর Gray code

এতক্ষণ বিট-নকশা মানেই ছিল একটি binary সংখ্যা। কিন্তু একই বিটগুলোকে অন্য নিয়মে অর্থ দেওয়া যায় — একে বলে কোড। তিনটি কোড digital logic-এর পরীক্ষায় আর বাস্তব hardware-এ বারবার আসে।

BCD (8421) — প্রতিটি দশমিক অঙ্ক আলাদা ৪ বিটে

BCD (Binary Coded Decimal)-এ পুরো সংখ্যাকে binary করা হয় না; প্রতিটি দশমিক অঙ্ককে আলাদাভাবে ৪ বিটে লেখা হয়, ওজন 8-4-2-1. ৮৫ → 1000 0101 (৮ আর ৫), যেখানে সাধারণ binary-তে ৮৫ = 101 0101. ৪ বিটে ১৬টি নকশা, কিন্তু ব্যবহার হয় মাত্র ১০টি — 1010 থেকে 1111 BCD-তে অবৈধ। জায়গা কিছুটা নষ্ট হয়, বিনিময়ে দশমিক দেখানো খুব সহজ: ক্যালকুলেটর, ডিজিটাল ঘড়ি, পেট্রোল পাম্পের মিটার বা বাসের ভাড়ার ডিসপ্লের ৭-segment অঙ্কে প্রতিটি nibble সরাসরি একটি অঙ্ক — ভাগ করে অঙ্ক বের করতে হয় না। BCD থেকে ৭-segment চালানোর decoder circuit পর্ব ১২-এ আসবে।

BCD যোগ আর +6 সংশোধন: দুটি BCD অঙ্ক সাধারণ binary নিয়মে যোগ করুন। যোগফল ৯-এর বেশি হলে (অর্থাৎ ফল একটি অবৈধ নকশা, অথবা nibble থেকে carry বেরিয়েছে) তার সাথে 0110 (৬) যোগ করুন। কেন ৬? কারণ ৪ বিটের nibble ১৬-তে উপচে পড়ে, আর দশমিক অঙ্ক উপচে পড়া উচিত ১০-এ — মাঝের ১৬ − ১০ = ৬টি অব্যবহৃত নকশা লাফিয়ে পার হতে ৬ যোগ করতে হয়। তখন যে carry বের হয়, সেটিই পরের দশমিক অঙ্কে যায়।

৪৭ + ৩৮ (BCD)

একক: 0111 + 1000 = 1111 (১৫ > ৯) → +0110 = 1 0101: লিখুন 0101, carry 1.
দশক: 0100 + 0011 + 1 = 1000 (৮ ≤ ৯), সংশোধন লাগে না।
উত্তর 1000 0101 = ৮৫।

৯ + ৯ (BCD)

1001 + 1001 = 1 0010 — দেখতে বৈধ 0010 (২), কিন্তু nibble থেকে carry বেরিয়েছে, মান ১৮ > ৯। তাই +0110 → 1 1000: উত্তর 0001 1000 = ১৮। শুধু “অবৈধ নকশা কি না” দেখলে এই ঘটনাটি ধরা পড়ত না।

Excess-3 — নিজেই নিজের পরিপূরক

Excess-3 কোডে প্রতিটি দশমিক অঙ্কের সাথে ৩ যোগ করে ৪ বিটে লেখা হয়: ০ → 0011, ৪ → 0111, ৯ → 1100. এর একটি সুন্দর গুণ: অঙ্ক d-এর ৯-এর পরিপূরক (৯ − d)-এর কোড পাওয়া যায় শুধু সব বিট উল্টে দিয়ে। ৪-এর কোড 0111, উল্টালে 1000 = ৫ + ৩ — অর্থাৎ ৯ − ৪ = ৫-এর কোড। কারণ d + ৩ আর (৯ − d) + ৩-এর যোগফল সবসময় ১৫ = 1111. এমন কোডকে বলে self-complementing code; পুরোনো দশমিক যন্ত্রে বিয়োগ করার জন্য এটি কাজে লাগত।

Gray code — প্রতি ধাপে ঠিক একটি বিট

সাধারণ binary-তে ৭ → ৮ যেতে চারটি বিটই বদলায় (0111 → 1000). বাস্তব circuit-এ চারটি বিট কখনো ঠিক একই মুহূর্তে বদলায় না; মাঝের এক ঝলকে যন্ত্র ভুল করে 1111 বা 0000 পড়তে পারে। Gray code-এ পরপর দুটি মানের মধ্যে ঠিক একটি বিট বদলায় — অর্থাৎ পাশাপাশি দুটি কোডের Hamming distance ১। তাই পড়ার মুহূর্তে বড়জোর পুরোনো বা নতুন মানটি আসে, কোনো তৃতীয় আজব মান নয়।

রূপান্তরের সূত্র

Binary → Gray: g = b ⊕ (b ≫ 1). অর্থাৎ MSB যেমন আছে তেমন, আর বাকি প্রতিটি Gray বিট = ঐ binary বিট ⊕ তার বাঁয়ের binary বিট। উদাহরণ: b = 1011 → 1011 ⊕ 0101 = 1110.
Gray → binary: বাঁ থেকে জমা XOR — bMSB = gMSB, তারপর bi = bi+1 ⊕ gi. 1110 → ১, ১⊕১ = ০, ০⊕১ = ১, ১⊕০ = ১ → 1011, আগের মান ফিরে এলো। সমানভাবে, b = g ⊕ (g ≫ 1) ⊕ (g ≫ 2) ⊕ …

কেন এক বিট বদলায় — ছোট প্রমাণ: b থেকে b + ১ হলে ডানের যে কয়টি 1 আছে সেগুলো 0 হয় আর ঠিক তার বাঁয়ের 0-টি 1 হয় — ধরুন অবস্থান k থেকে নিচ পর্যন্ত সব বিট উল্টায়। gi = bi ⊕ bi+1; i < k হলে bi আর bi+1 দুটোই উল্টায়, তাই তাদের XOR বদলায় না। i = k হলে bk উল্টায় কিন্তু bk+1 উল্টায় না, তাই gk বদলায়। i > k হলে কিছুই বদলায় না। ফলে ঠিক একটি Gray বিট — gk — বদলায়।

কোথায় লাগে? কোণ মাপার rotary encoder-এর চাকতিতে, K-map-এর সারি-কলামের ক্রমে (00, 01, 11, 10 — পর্ব ০৯), আর ভিন্ন clock-এ চলা দুটি circuit-এর মধ্যে counter-এর মান পাঠাতে (পর্ব ১৮)। বিস্তারিত ইতিহাস আর ব্যবহার নিচের ভাবনার প্রশ্ন ৫-এ।

নিচের টুলটি BCD যোগ ঘর ধরে ধরে +6 সংশোধনসহ দেখায়, তারপর ০–১৫-এর Gray টেবিল বানায় — প্রতিটি সারিতে আগের Gray কোড থেকে কয়টি বিট বদলাল (১৫ থেকে ০-তে ফিরে আসাও ধরে) আর Gray থেকে ফিরিয়ে আনা মান:

// BCD (8421) addition with the +6 correction, and the 4-bit Gray code table.
const nib = (x) => x.toString(2).padStart(4, "0");
const toBCD = (n) => String(n).split("").map((c) => nib(+c)).join(" ");
function bcdAdd(a, b) {
  const len = Math.max(String(a).length, String(b).length);
  const A = String(a).padStart(len, "0"), B = String(b).padStart(len, "0");
  let carry = 0, out = [];
  for (let i = len - 1; i >= 0; i--) {     // rightmost digit first
    let s = +A[i] + +B[i] + carry;
    let line = `  ${nib(+A[i])} + ${nib(+B[i])} + ${carry} = ${s.toString(2).padStart(5, "0")} (${s})`;
    if (s > 9) { s += 6; line += ` > 9, +0110 -> ${s.toString(2).padStart(5, "0")}`; }
    carry = s >> 4; out.unshift(nib(s & 15));
    console.log(line + `  keep ${nib(s & 15)}, carry ${carry}`);
  }
  if (carry) out.unshift("0001");
  return out.join(" ");
}
for (const [a, b] of [[47, 38], [9, 9]]) {
  console.log(`${a} = ${toBCD(a)}, ${b} = ${toBCD(b)}`);
  console.log(`${a} + ${b} in BCD = ${bcdAdd(a, b)}  (BCD of ${a + b}: ${toBCD(a + b)})`);
}
console.log(`plain binary 85 = ${(85).toString(2)} -- a different pattern`);
const gray = (b) => b ^ (b >> 1);
function grayToBin(g) { let b = 0; for (; g; g >>= 1) b ^= g; return b; }
console.log("\n n  binary  gray  bits changed  back");
let prev = gray(15);                        // wrap-around: 15 -> 0
for (let n = 0; n < 16; n++) {
  const g = gray(n), diff = (g ^ prev).toString(2).split("1").length - 1;
  console.log(`${String(n).padStart(2)}   ${nib(n)}   ${nib(g)}      ${diff}        ${grayToBin(g)}`);
  prev = g;
}

আউটপুট (নির্ধারিত; Gray টেবিলের মাঝের সারিগুলো ছোট করে দেখানো):

47 = 0100 0111, 38 = 0011 1000
  0111 + 1000 + 0 = 01111 (15) > 9, +0110 -> 10101  keep 0101, carry 1
  0100 + 0011 + 1 = 01000 (8)  keep 1000, carry 0
47 + 38 in BCD = 1000 0101  (BCD of 85: 1000 0101)
9 = 1001, 9 = 1001
  1001 + 1001 + 0 = 10010 (18) > 9, +0110 -> 11000  keep 1000, carry 1
9 + 9 in BCD = 0001 1000  (BCD of 18: 0001 1000)
plain binary 85 = 1010101 -- a different pattern

 n  binary  gray  bits changed  back
 0   0000   0000      1        0
 1   0001   0001      1        1
 2   0010   0011      1        2
 3   0011   0010      1        3
 4   0100   0110      1        4
 ...
 7   0111   0100      1        7
 8   1000   1100      1        8
 ...
14   1110   1001      1        14
15   1111   1000      1        15

৪৭ + ৩৮-এ এককের ঘরে কাঁচা যোগফল ১৫, তাই +6 যোগে 10101 — নিচের ৪ বিট 0101 (৫) রেখে carry গেল দশকে; দশকে ৮, সংশোধন লাগেনি। ৯ + ৯-এ কাঁচা যোগফল 10010 = ১৮ — nibble থেকে carry বেরিয়েছে, তাই এখানেও +6. Gray টেবিলের “bits changed” কলামে প্রতিটি সারিতে 1, এমনকি ৭ → ৮ (0100 → 1100) আর ১৫ → ০ (1000 → 0000)-তেও — অর্থাৎ ৪ বিটের Gray code একটি বৃত্তের মতো, শেষ থেকে শুরুতে ফিরতেও এক বিট। শেষ কলাম দেখায় Gray থেকে binary-তে ফেরার সূত্রটি প্রতিটি মানে ঠিক আগের সংখ্যাটি ফেরত দেয়।

আরও গভীরে: BCD এখনো হারিয়ে যায়নি। x86-এর পুরোনো DAA (Decimal Adjust after Addition) instruction ঠিক এই +6 সংশোধন hardware-এ করত; ৬৪-বিট mode-এ এটি আর বৈধ নয়, কিন্তু ৩২-বিট mode-এ এখনো আছে। IBM-এর mainframe আর POWER প্রসেসরে দশমিক হিসাবের জন্য আলাদা hardware রাখা হয়েছে, কারণ ব্যাংক আর বীমার হিসাবে ০.১ টাকাকে ঠিক ০.১-ই রাখা জরুরি; এজন্য IEEE 754-2008 মানে দশমিক floating point-ও যুক্ত হয়েছে।

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • (0.1)10 কি binary-তে ঠিকভাবে রাখা যায়? — না; ১/১০-এর হরে ৫ আছে, যা ২-কে ভাগ করে না, তাই 0.0(0011)… অসীম পর্যাবৃত্ত।
  • ০ থেকে ১০০০ রাখতে কত বিট? — ⌈log2১০০১⌉ = ১০ বিট (২১০ = ১০২৪)।
  • Binary 1011-এর Gray code কত? — 1011 ⊕ 0101 = 1110.
  • BCD যোগে কখন +6 যোগ করা হয়, আর কেন? — যোগফল ৯-এর বেশি বা nibble থেকে carry বের হলে; ১৬ আর ১০-এর পার্থক্য ৬টি অব্যবহৃত নকশা পার হতে।
  • ৫০০ GB ডিস্ক কেন প্রায় ৪৬৫ GB দেখায়? — ৫০০ × ১০৯ ÷ ২৩০ ≈ ৪৬৫.৬৬; বাক্সে দশমিক GB, OS-এ binary GiB.

১০ · AI-এর যুগে — রূপান্তর নিজে যাচাই করুন

সংখ্যা রূপান্তরের প্রশ্নে AI-এর উত্তর বেশিরভাগ সময় ঠিক থাকে — কিন্তু সবসময় নয়। ভাষা মডেল হিসাব করে না, পরের শব্দ অনুমান করে — তাই লম্বা binary সারিতে একটি বিট বাদ পড়া, ভগ্নাংশের দলে ভুল দিকে 0 বসানো, বা GB আর GiB গুলিয়ে ফেলা খুব স্বাভাবিক ভুল।

AI প্রায়ই যা ভুল করেকীভাবে ধরবেন
লম্বা binary-তে একটি বিট বাদ বা বেশি (যেমন ১২ বিটের জায়গায় ১১)বিট গুনুন; Horner দিয়ে দশমিকে ফিরিয়ে মিলান
ভগ্নাংশের octal/hex দলে padding বাঁ দিকে বসানোভগ্নাংশে 0 সবসময় ডানে — দশমিকে ফিরিয়ে দেখুন
০.১-এর মতো অসীম ভগ্নাংশকে কয়েক ঘরে “ঠিক” বলে দাবি করাহর-এর মৌলিক উৎপাদক যাচাই; বলুন কত ঘরে কাটা হলো
BCD যোগে carry বের হওয়ার ঘটনায় +6 ভুলে যাওয়া (৯ + ৯)যোগফল > ৯ পরীক্ষা করুন, শুধু “অবৈধ নকশা” নয়
GB আর GiB, Gb (bit) আর GB (byte) গুলিয়ে ফেলাএককের পুরো নাম লিখে হিসাব; ৮ বিট = ১ byte
C-তে 010-কে দশ ধরে নেওয়া, বা 0b-কে সব compiler-এর জন্য নিশ্চিত বলাকোড চালিয়ে দেখুন; compiler আর C standard-এর version যাচাই

AI-কে শুধু উত্তর না চেয়ে প্রতিটি ধাপ আর একটি স্বাধীন যাচাই চাইলে ভুল সহজে ধরা পড়ে:

আমি সংখ্যা পদ্ধতির রূপান্তর অনুশীলন করছি। (347.25)10-কে binary, octal আর hexadecimal-এ রূপান্তর করো। ১. পূর্ণ অংশের প্রতিটি ভাগ (ভাগফল, ভাগশেষ) আলাদা লাইনে দেখাও। ২. ভগ্নাংশের প্রতিটি গুণ দেখাও, আর বলো ভগ্নাংশটি সসীম কেন বা কেন নয়। ৩. octal আর hex-এর জন্য binary থেকে দল বানিয়ে দেখাও — padding কোন দিকে বসালে, স্পষ্ট লেখো। ৪. শেষে প্রতিটি উত্তরকে আবার দশমিকে ফিরিয়ে দেখাও যে মিলেছে। নিশ্চিত না হলে অনুমান করো না, বলো।

AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১প্রথম দিকের কিছু বিখ্যাত কম্পিউটার দশমিকে কাজ করত। তাহলে binary শেষ পর্যন্ত জিতল কেন — আর অন্য ভিত্তি (যেমন ৩) কি কখনো চেষ্টা করা হয়েছে?

১৯৪০-এর দশকের মাঝামাঝি তৈরি ENIAC ভেতরে দশমিকে হিসাব করত — প্রতিটি দশমিক অঙ্ক রাখার জন্য দশটি অবস্থার একটি “ring counter”, যা বানাতে অনেক vacuum tube লাগত। প্রায় একই সময়ে ১৯৪৫ সালে John von Neumann-এর লেখা EDVAC-এর বিখ্যাত খসড়া প্রতিবেদনে binary ব্যবহারের পক্ষে যুক্তি দেওয়া হয়: দুই অবস্থার উপাদান সরল, সস্তা আর নির্ভরযোগ্য, আর হিসাবের circuit অনেক ছোট হয়।

যুক্তিটি আজও একই। দুই স্তরের সংকেতে noise margin সবচেয়ে বড়, তাই কোটি কোটি transistor-এর chip-এও প্রায় ভুলহীনভাবে চলে। Boolean algebra সরাসরি circuit-এ বসে যায়। আর binary-তে গুণের টেবিল মাত্র ০ আর ১ — তাই multiplier শুধু shift আর যোগ।

তবে দশমিক যন্ত্র পুরোপুরি হারায়নি — IBM-এর কিছু ব্যবসায়িক কম্পিউটার (যেমন 1401-এর পরিবার) দশমিক অঙ্ক-ভিত্তিক ছিল। ভিত্তি ৩-এর পরীক্ষাও হয়েছে: ১৯৫৮ সালে মস্কো স্টেট বিশ্ববিদ্যালয়ে তৈরি Setun “balanced ternary” (অঙ্ক −১, ০, +১) ব্যবহার করত। তাত্ত্বিকভাবে সংখ্যা রাখার খরচ সবচেয়ে কম ভিত্তি e ≈ ২.৭১৮-এর কাছে, তাই ৩ কাগজে-কলমে একটু দক্ষ; কিন্তু তিনটি স্থিতিশীল ভোল্টেজ স্তর নির্ভরযোগ্যভাবে বানানো কঠিন, আর শিল্প binary-র পেছনে দাঁড়িয়ে যাওয়ায় ternary টেকেনি।

মজার ব্যাপার, একাধিক স্তর অন্য জায়গায় ফিরে এসেছে: আধুনিক flash মেমরির একটি cell-এ ৪, ৮ বা ১৬টি চার্জ-স্তর রেখে ২, ৩ বা ৪ বিট রাখা হয় (MLC, TLC, QLC) — বিনিময়ে বেশি ভুল-সংশোধন কোড লাগে।

মূল উপলব্ধি: binary গাণিতিকভাবে “সেরা” বলে নয়, প্রকৌশলের দিক থেকে সবচেয়ে নির্ভরযোগ্য বলে জিতেছে। যেখানে ঘনত্ব বেশি দামি, সেখানে আজও বেশি স্তর ব্যবহার হয় — noise-এর বিনিময়ে।

প্র ০২একসময় octal খুব চলত, এখন প্রায় সব জায়গায় hex. এই বদলের কারণ কী, আর octal এখনো কোথায় টিকে আছে?

উত্তরটি লুকিয়ে আছে word size-এ — একটি কম্পিউটার একবারে কত বিট নিয়ে কাজ করে। ১৯৫০–৬০-এর দশকের অনেক বিখ্যাত যন্ত্রের word ছিল ১২, ১৮, ২৪ বা ৩৬ বিট — যেমন DEC-এর PDP-8 (১২ বিট), PDP-1 (১৮ বিট), আর IBM 7090-এর পরিবার (৩৬ বিট)। এই সংখ্যাগুলো সব ৩ দিয়ে বিভাজ্য, তাই octal-এর ৩ বিটের দলে word নিখুঁতভাবে ভাগ হয়ে যেত: ১২ বিট = ৪টি octal অঙ্ক।

মোড় ঘোরে ১৯৬৪ সালে IBM-এর System/360 দিয়ে। এটি ৮ বিটের byte আর ৩২ বিটের word জনপ্রিয় করে। ৮ আর ৩২ ৩ দিয়ে বিভাজ্য নয়, কিন্তু ৪ দিয়ে বিভাজ্য — তাই একটি byte = ঠিক দুটি hex অঙ্ক। octal-এ একটি byte লিখলে শেষ দলটি অসম্পূর্ণ থাকে (৩ + ৩ + ২), আর দুটি byte পাশাপাশি রাখলে octal অঙ্কগুলো byte-এর সীমানা পেরিয়ে যায় — memory dump পড়া কঠিন হয়। ৮-বিট byte সর্বজনীন হওয়ার সাথে সাথে hex-ও মানক হয়ে যায়।

তবু octal কয়েকটি জায়গায় শক্তভাবে টিকে আছে। সবচেয়ে চেনা Unix-এর file permission: owner, group আর others — প্রত্যেকের জন্য তিনটি বিট (read, write, execute), তাই প্রতিটি দল ঠিক একটি octal অঙ্ক। chmod 755 মানে 111 101 101 — owner সব পারেন, বাকিরা পড়তে আর চালাতে পারেন। দ্বিতীয়টি একটি ঐতিহাসিক উত্তরাধিকার: C ভাষায় শুরুতে 0 দিলে সংখ্যাটি octal. এটি PDP-যুগের সুবিধা ছিল, কিন্তু আজ একটি পরিচিত ফাঁদ — কেউ সুন্দর করে সাজাতে int code = 010; লিখলে মান হয় ৮। এজন্য Python 3 পুরোনো 010 লেখাকে নিষিদ্ধ করে স্পষ্ট 0o10 চালু করেছে, আর JavaScript-এর strict mode-এও পুরোনো ধরনের octal লেখা চলে না।

মূল উপলব্ধি: সংখ্যা লেখার ধরন hardware-এর আকার মেনে চলে। Byte ৮ বিটের হওয়ায় hex জিতেছে; যেখানে তথ্য স্বাভাবিকভাবে ৩ বিটের দলে আসে, সেখানে octal আজও সবচেয়ে পরিষ্কার।

প্র ০৩০.১ binary-তে শেষ হয় না — এই ছোট্ট গাণিতিক সত্য বাস্তবে কি কখনো বড় বিপদ ঘটিয়েছে? টাকা-পয়সার সফটওয়্যার এটি কীভাবে এড়ায়?

সবচেয়ে বেশি উদ্ধৃত ঘটনাটি ১৯৯১ সালের উপসাগরীয় যুদ্ধের। সৌদি আরবের ধাহরানে একটি Patriot ক্ষেপণাস্ত্র-প্রতিরোধ ব্যবস্থা আগত একটি Scud ক্ষেপণাস্ত্র ঠেকাতে ব্যর্থ হয়, আর একটি সামরিক ব্যারাকে আঘাতে ২৮ জন মার্কিন সৈন্য নিহত হন। পরে মার্কিন সরকারি তদন্ত সংস্থা GAO-র প্রতিবেদনে কারণ হিসেবে একটি সময়-হিসাবের ত্রুটির কথা আসে: যন্ত্রটি সময় গুনত এক সেকেন্ডের দশ ভাগের এক ভাগ এককে, আর ০.১-কে রাখত একটি ২৪-বিটের fixed-point register-এ। ০.১ binary-তে অসীম, তাই কেটে রাখা মানে প্রতি tick-এ প্রায় ০.০০০০০০০৯৫ সেকেন্ডের ভুল। একটানা প্রায় ১০০ ঘণ্টা চলার পর সেই ভুল জমে দাঁড়ায় প্রায় ০.৩৪ সেকেন্ড — আর দ্রুতগামী Scud এই সময়ে আধা কিলোমিটারের বেশি পথ পেরিয়ে যায় — ফলে radar ভুল জায়গায় খুঁজেছিল। সমস্যাটি আগেই জানা ছিল এবং সংশোধিত সফটওয়্যার পাঠানোর পথে ছিল।

শিক্ষা: “খুব ছোট ভুল” আর “কোটি বার জমা হওয়া খুব ছোট ভুল” এক জিনিস নয়।

টাকা-পয়সার জগৎ এজন্য binary ভগ্নাংশ এড়িয়ে চলে। প্রচলিত কৌশল তিনটি। (১) ছোট এককে পূর্ণসংখ্যা: টাকা না রেখে পয়সা রাখা — ১২৩.৪৫ টাকা মানে পূর্ণসংখ্যা ১২৩৪৫, যা binary-তে নিখুঁত। মোবাইল ব্যাংকিং বা পেমেন্ট সিস্টেম সাধারণত এভাবেই নকশা করা হয়। (২) দশমিক ধরনের সংখ্যা: Java-র BigDecimal, Python-এর decimal, SQL-এর DECIMAL/NUMERIC ভেতরে দশমিক অঙ্ক রাখে। (৩) Hardware-এ দশমিক: IEEE 754-2008 মানে দশমিক floating point যুক্ত হয়েছে, আর IBM-এর কিছু প্রসেসর এটি সরাসরি hardware-এ চালায়।

মূল উপলব্ধি: কোন ভিত্তিতে সংখ্যা রাখছেন, সেটি একটি নকশার সিদ্ধান্ত — গণিত বলে দেয় কোন মান হারাবে। পর্ব ২৩–২৪-এ দেখবেন এই হারানো মান কীভাবে মাপা আর নিয়ন্ত্রণ করা হয়।

প্র ০৪RAM সবসময় ৪, ৮, ১৬ GB — ২-এর ঘাতে — কিন্তু ডিস্ক ৫০০ GB বা ১ TB-এর মতো গোল দশমিক মাপে বিক্রি হয়। এই পার্থক্যের প্রকৌশলগত কারণ কী?

RAM-এর আকার ২-এর ঘাত হওয়ার কারণ address. মেমরির প্রতিটি byte-কে একটি নম্বর (ঠিকানা) দিয়ে খোঁজা হয়, আর সেই নম্বর আসে কিছু address তারের মাধ্যমে — প্রতিটি তার একটি বিট। n টি তার থাকলে আলাদা ঠিকানা ঠিক ২nটি। Decoder circuit (পর্ব ১২) n বিট থেকে ২nটি লাইনের একটি বেছে নেয়। ২n-এর কম ঘর রাখলে কিছু ঠিকানা অর্থহীন থাকে, বেশি রাখতে চাইলে আরেকটি তার লাগে — তাই RAM chip আর module স্বাভাবিকভাবেই ২-এর ঘাতে আসে (যদিও আধুনিক module-এ কখনো কখনো ২৪ GB-এর মতো মাপও দেখা যায়, যেখানে ভেতরের chip-এর ঘনত্ব ২-এর ঘাত নয়)।

ডিস্ক বা SSD-র ক্ষেত্রে ব্যাপারটি আলাদা। Hard disk-এ ধারণক্ষমতা নির্ভর করে platter-এর সংখ্যা, track-এর ঘনত্ব আর একটি track-এ কতগুলো sector ধরে তার ওপর — এর কোনোটিই ২-এর ঘাতে বাঁধা নয়। তাই নির্মাতারা SI মানে (১ GB = ১০৯ byte) মাপ লেখেন, যা বৈজ্ঞানিকভাবে সঠিক এবং সংখ্যাটিও বড় দেখায়। SSD-র ভেতরের flash chip ২-এর ঘাতে আসলেও তার একটি অংশ লুকানো থাকে (over-provisioning), বাকিটা দশমিক মাপে বিক্রি হয়।

এই দ্ব্যর্থতা নিয়ে যুক্তরাষ্ট্রে কয়েকটি ডিস্ক নির্মাতার বিরুদ্ধে মামলাও হয়েছিল বলে জানা যায়। আজ Linux-এর অনেক টুল GiB লেখে, macOS দশমিক GB দেখায়, আর Windows ২-এর ঘাতে হিসাব করেও “GB” লেখে — একই ডিস্ক তিন জায়গায় তিন রকম।

মূল উপলব্ধি: যে জিনিস address দিয়ে বাছাই হয় (RAM, cache, page) তা ২-এর ঘাতে আসে; যে জিনিস পদার্থের ঘনত্বে মাপা হয় (ডিস্ক, নেটওয়ার্কের গতি) তা দশমিকে। একক না লিখে কোনো সংখ্যা বলাই এখানে আসল ভুল।

প্র ০৫Gray code কে, কেন আবিষ্কার করলেন? “এক বিট বদলায়” — এই সরল ধর্ম আজকের chip নকশায় কোথায় কোথায় কাজে লাগে?

কোডটির নাম Bell Labs-এর গবেষক Frank Gray-এর নামে। তিনি ১৯৪০-এর দশকের শেষে pulse code modulation (কথাকে সংখ্যায় বদলে পাঠানো)-এর একটি vacuum tube যন্ত্রের পেটেন্টে এটি বর্ণনা করেন — আবেদন ১৯৪৭ সালে, অনুমোদন ১৯৫৩ সালে, শিরোনাম “Pulse code communication”. সেখানে এর নাম ছিল “reflected binary code”, কারণ n বিটের কোড বানানো যায় (n−1) বিটের তালিকাটি আয়নার মতো উল্টে নিচে বসিয়ে: ওপরের অর্ধেকের শুরুতে 0, নিচের অর্ধেকের শুরুতে 1. ধারণাটি তার আগেও কিছু যান্ত্রিক কাজে ব্যবহৃত হয়েছিল বলে জানা যায়, কিন্তু নামটি Gray-এর নামেই স্থায়ী হয়েছে।

সমস্যাটি ছিল analog থেকে digital রূপান্তরে: ভোল্টেজ মাপার মুহূর্তে যদি কয়েকটি বিট একসাথে বদলাতে থাকে, আর সবগুলো ঠিক একই সময়ে স্থির না হয়, তাহলে পড়া মান সম্পূর্ণ আজব হতে পারে — ৭ আর ৮-এর মাঝে পড়ে ১৫। এক বিটের কোডে এই ঝুঁকি থাকে না: বড়জোর পুরোনো বা নতুন মান।

আজকের ব্যবহার:

  • Rotary encoder: মোটরের শ্যাফট বা শিল্পকারখানার রোবট বাহুর কোণ মাপতে চাকতির ওপর Gray-কোডের দাগ।
  • K-map: সারি-কলাম 00, 01, 11, 10 ক্রমে, যাতে পাশাপাশি ঘর একটি চলকে আলাদা — এই পাশাপাশি থাকাটাই সরলীকরণের ভিত্তি (পর্ব ০৯)।
  • Clock domain crossing: ভিন্ন clock-এ চলা দুটি অংশের মাঝে FIFO (আগে ঢোকা ডেটা আগে বের হয় এমন queue)-র pointer Gray code-এ পাঠানো হয়। গ্রহণকারী ভুল মুহূর্তে পড়লেও একটি বিটই অনিশ্চিত, তাই মান বড়জোর এক ঘর পুরোনো (পর্ব ১৮)।
  • শক্তি সাশ্রয়: address bus-এ পরপর ঠিকানা Gray-তে পাঠালে কম তারের অবস্থা বদলায়, আর CMOS-এ শক্তি খরচ হয় মূলত অবস্থা বদলের সময় — এ নিয়ে গবেষণা আছে।

মূল উপলব্ধি: একটি কোডের মূল্য শুধু কত বিটে কী রাখা যায় তাতে নয়, পরিবর্তনের মুহূর্তে কী ঘটে তাতেও। Hardware-এ “মাঝপথের অবস্থা” একটি বাস্তব সমস্যা, আর Gray code সেটিকে গণিত দিয়েই সমাধান করে।

অনুশীলন

পরীক্ষার খাতার মতো করে পুরো ধাপ লিখে সমাধান করুন, তারপর উত্তর মিলিয়ে দেখুন। উপরের converter-এ নিজের উত্তর যাচাইও করতে পারেন।

  1. পূর্ণ রূপান্তর: (347.25)10-কে binary, octal আর hexadecimal-এ রূপান্তর করুন। প্রতিটি উত্তর দশমিকে ফিরিয়ে যাচাই করুন।

    পূর্ণ অংশ ৩৪৭ (বারবার ২ দিয়ে ভাগ): ৩৪৭ → ১৭৩ (1), ১৭৩ → ৮৬ (1), ৮৬ → ৪৩ (0), ৪৩ → ২১ (1), ২১ → ১০ (1), ১০ → ৫ (0), ৫ → ২ (1), ২ → ১ (0), ১ → ০ (1). নিচ থেকে ওপরে: 101011011.

    ভগ্নাংশ ০.২৫ (বারবার ২ দিয়ে গুণ): ০.২৫ × ২ = ০.৫ (0), ০.৫ × ২ = ১.০ (1), বাকি ০ — থামুন। .01. সসীম, কারণ ০.২৫ = ১/৪ আর ৪ = ২২.

    Binary: (101011011.01)2.

    Octal (৩ বিটের দল, বিন্দু থেকে): 101 011 011 . 010 (ভগ্নাংশে ডানে একটি 0) → (533.2)8. যাচাই: ৫·৬৪ + ৩·৮ + ৩ = ৩৪৭, আর ২/৮ = ০.২৫।

    Hex (৪ বিটের দল): 0001 0101 1011 . 0100 → (15B.4)16. যাচাই: ১·২৫৬ + ৫·১৬ + ১১ = ৩৪৭, আর ৪/১৬ = ০.২৫।

  2. অপরিচিত ভিত্তি আর বিটের হিসাব: (ক) (213)4-কে ভিত্তি ৬-এ নিন। (খ) (A3.4)16-কে octal-এ নিন। (গ) ৫০০০টি আলাদা পণ্যকে আলাদা কোড দিতে কমপক্ষে কত বিট লাগবে? (ঘ) একটি “২ TB” ডিস্ক OS-এ কত GiB দেখাবে?

    (ক) Horner: ২ → ২·৪ + ১ = ৯ → ৯·৪ + ৩ = ৩৯। এবার ৩৯-কে ৬ দিয়ে ভাগ: ৩৯ → ৬ (ভাগশেষ ৩), ৬ → ১ (০), ১ → ০ (১). উত্তর (103)6. যাচাই: ৩৬ + ০ + ৩ = ৩৯।

    (খ) দুটিই ২-এর ঘাত, তাই binary সেতু: A3.4 → 1010 0011 . 0100. ৩ বিটের দল: পূর্ণ অংশ 1010 0011 → বাঁয়ে একটি 0 বসিয়ে 010 100 011; ভগ্নাংশ 0100 → ডানে দুটি 0 বসিয়ে 010 000 = .20 = .2 (ডানের 0 মান বদলায় না) → (243.2)8. যাচাই: ১৬৩.২৫ = ২·৬৪ + ৪·৮ + ৩ + ২/৮।

    (গ) দরকার ২n ≥ ৫০০০। ২১২ = ৪০৯৬ কম, ২১৩ = ৮১৯২ যথেষ্ট — ১৩ বিট. (০ থেকে ৪৯৯৯ কোড ধরলে ⌈log2৫০০০⌉ = ১৩, একই উত্তর।)

    (ঘ) ২ × ১০১২ ÷ ২৩০ ≈ ১৮৬২.৬ GiB, অর্থাৎ প্রায় ১.৮২ TiB (২ × ১০১২ ÷ ২৪০)।

  3. BCD আর Gray হাতে-কলমে: (ক) ৫৮ + ৬৭ BCD-তে যোগ করুন — প্রতিটি ঘরের সংশোধন দেখিয়ে। (খ) Binary 1101 0110-এর Gray code কত? (গ) Gray 1011-কে binary-তে ফেরান।

    (ক) ৫৮ = 0101 1000, ৬৭ = 0110 0111. একক: 1000 + 0111 = 1111 (১৫ > ৯) → +0110 = 1 0101: লিখুন 0101, carry 1. দশক: 0101 + 0110 + 1 = 1100 (১২ > ৯) → +0110 = 1 0010: লিখুন 0010, carry 1. শেষ carry নতুন অঙ্ক 0001. উত্তর 0001 0010 0101 = ১২৫। দুই ঘরেই সংশোধন লাগল।

    (খ) g = b ⊕ (b ≫ 1) = 11010110 ⊕ 01101011 = 1011 1101.

    (গ) b3 = ১; b2 = ১ ⊕ ০ = ১; b1 = ১ ⊕ ১ = ০; b0 = ০ ⊕ ১ = ১ → 1101 (১৩)। যাচাই: ১৩-এর Gray = 1101 ⊕ 0110 = 1011.

  4. আউটপুট অনুমান করুন, তারপর কোড লিখুন: (ক) নিচের লাইনটি কী ছাপবে? printf("%d %o %x %X %d\n", 010, 64, 255, 3054, 0x1F); (খ) যেকোনো ভাষায় একটি ফাংশন লিখুন যা একটি স্ট্রিং আর ভিত্তি (২–১৬) নিয়ে Horner-এর পদ্ধতিতে দশমিক মান ফেরত দেয়, আর অবৈধ অঙ্ক পেলে ভুল জানায়।

    (ক) 8 100 ff BEE 31. কারণ: 010 octal = ৮; ৬৪ octal-এ 100 (৮২); ২৫৫ hex-এ ff (ছোট হাতের, কারণ %x); ৩০৫৪ = ১১·২৫৬ + ১৪·১৬ + ১৪ = BEE (বড় হাতের %X); 0x1F = ৩১।

    (খ) একটি নমুনা (JavaScript):

    function parseBase(s, r) {
      const DIG = "0123456789ABCDEF";
      let v = 0;
      for (const c of s.toUpperCase()) {
        const d = DIG.indexOf(c);
        if (d < 0 || d >= r) throw new Error(`invalid digit ${c} for base ${r}`);
        v = v * r + d;               // Horner step
      }
      return v;
    }
    // parseBase("4231", 5) -> 566,  parseBase("1012", 2) -> error

    মূল বিষয় দুটি: প্রতিটি অঙ্ক r-এর চেয়ে ছোট কি না যাচাই (ভিত্তি ২-এ “2” অবৈধ), আর v = v·r + d. খুব বড় সংখ্যায় JavaScript-এর সাধারণ সংখ্যা ২৫৩-এর পরে নির্ভুলতা হারায় — তখন BigInt লাগবে।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএই মাপগুলো ছোট থেকে বড় ক্রমে সাজান (▲▼ চেপে সরান)

  1. ১ KB (১০০০ byte)
  2. ১ KiB (১০২৪ byte)
  3. ১ MB (১০৬ byte)
  4. ১ MiB (২২০ byte)
  5. ১ GB (১০৯ byte)
  6. ১ GiB (২৩০ byte)

(1011.011)2-এর দশমিক মান কত?

  • ১১.৩৭৫
  • ১১.৭৫
  • ১৩.৩৭৫
  • ১১.০১১

নিচের কোন দশমিক ভগ্নাংশ binary-তে সসীম সংখ্যক বিটে ঠিক ঠিক লেখা যায়?

  • ০.১
  • ০.২
  • ০.৩৭৫
  • ০.৩

(7F3)16-কে octal-এ লিখলে কী হবে?

  • (7F3)8 — F octal-এ বৈধ নয়, তাই এটি অসম্ভব
  • (3763)8
  • (7746)8
  • (2035)8

০ থেকে ১০০০ পর্যন্ত (দুই প্রান্তসহ) প্রতিটি পূর্ণসংখ্যাকে unsigned binary-তে রাখতে কমপক্ষে কত বিট লাগবে?

  • ৮
  • ৯
  • ১১
  • ১০

BCD-তে ৫ + ৮ যোগ করতে 0101 + 1000 করলে 1101 আসে। সঠিক BCD উত্তর পেতে কী করতে হবে?

  • কিছুই না, 1101-ই উত্তর
  • 0110 যোগ করে 0001 0011
  • 0011 যোগ করে 1 0000
  • 0110 বিয়োগ করে 0111

৪ বিটের binary 0110-এর Gray code কোনটি?

  • 0101
  • 0110
  • 0111
  • 0100

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. ভিত্তি r-এ মান = Σ di·ri, অঙ্ক ০ থেকে r−1; hardware binary বেছে নেয় noise margin আর সরলতার জন্য।
  2. ভিত্তি r → দশমিক: Horner, v = v·r + d. দশমিক → ভিত্তি r: পূর্ণ অংশে বারবার ভাগ (ভাগশেষ উল্টো ক্রমে), ভগ্নাংশে বারবার গুণ (পূর্ণ অংশ সোজা ক্রমে)।
  3. p/q সসীম হয় কেবল যদি q-এর প্রতিটি মৌলিক উৎপাদক ভিত্তিকে ভাগ করে — তাই ০.১ binary-তে অসীম।
  4. Binary ↔ octal/hex: বিন্দু থেকে ৩/৪ বিটের দল; পূর্ণ অংশে বাঁয়ে, ভগ্নাংশে ডানে 0. N পর্যন্ত রাখতে ⌈log2(N+1)⌉ বিট; GB ≠ GiB.
  5. BCD যোগে ফল > ৯ হলে +0110; Gray: g = b ⊕ (b ≫ 1), পাশাপাশি মানে এক বিট বদলায়।

পরের পর্বে: এ পর্যন্ত সব সংখ্যা ছিল ধনাত্মক। কিন্তু −৫ রাখবেন কীভাবে? Sign-magnitude, one's complement আর two's complement — আর কেন আধুনিক প্রায় সব CPU শেষেরটি বেছে নিয়েছে, কীভাবে বিয়োগ যোগে বদলে যায়, আর overflow কখন ঘটে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান