পাঠ ১৯ · ৫৭-এর মধ্যে · মডিউল ৪
Home / Courses / Computer Architecture & Digital Logic / অ্যাডিশন-সাবট্রাকশন সার্কিট

বাইনারি অ্যাডিশন ও সাবট্রাকশন সার্কিট

Binary addition & subtraction circuits
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • কীভাবে L09-এর ripple-carry adder সরাসরি L18-এর two's complement বিট-প্যাটার্নের উপর কাজ করে
  • $5+(-3)=2$ — সম্পূর্ণ বিট-লেভেল অ্যাডার ট্রেস দিয়ে বাস্তবে গণনা করে যাচাই করা (হাত দিয়ে ফলাফল বসিয়ে নয়)
  • $5+5$-এর মতো একটি genuine ওভারফ্লো কেস সনাক্ত করা, L18-এর carry-in/carry-out নিয়ম প্রয়োগ করে
  • একটিই হার্ডওয়্যার ইউনিট (adder/subtractor) কীভাবে যোগ ও বিয়োগ — দুটোই চালায়, কোনো অতিরিক্ত সার্কিট ছাড়াই

১ · দুটি পাঠের সংশ্লেষণ

এতদিন L09 আর L18 আলাদা আলাদা পাঠ ছিল — L09 শিখিয়েছিল কীভাবে ফুল অ্যাডারL08 — তিনটি বিট (A, B, Cin) যোগ করে Sum ও Cout দেয়। চেইন করে একটি রিপল ক্যারি অ্যাডার বানানো যায়, আর কীভাবে B-কে ইনভার্ট করে Cin=1 দিলে বিয়োগও করা যায়। L18 শিখিয়েছিল ঋণাত্মক সংখ্যাকে Two's Complement বিট-প্যাটার্নে এনকোড করার নিয়ম। এই পাঠে দুটোকে একসাথে জোড়া দেওয়া হচ্ছে — L18-এর বিট-প্যাটার্নগুলো সরাসরি L09-এর অ্যাডার সার্কিটে ঢুকিয়ে, প্রকৃত সাইনড যোগ-বিয়োগ বাস্তবায়ন করা হবে।

সবচেয়ে গুরুত্বপূর্ণ অন্তর্দৃষ্টিটি হলো — CPU-এর অ্যাডার সার্কিট নিজে "জানে না" সে যোগ করছে না ঋণাত্মক সংখ্যা যোগ করছে। সে শুধু বিট-প্যাটার্নগুলো ফুল-অ্যাডার চেইনের মধ্য দিয়ে পাঠায়। Two's complement এমনভাবে ডিজাইন করা যে এই একই, সরল প্রক্রিয়া স্বয়ংক্রিয়ভাবে সঠিক সাইনড ফলাফল দেয় — কোনো বাড়তি "if সংখ্যাটি ঋণাত্মক হলে..." যুক্তির প্রয়োজন নেই।

২ · ওয়ার্কড উদাহরণ — $5 + (-3) = 2$

4-বিট two's complement ব্যবহার করে —

  • $5 \to 0101$
  • $-3 \to 1101$ (L18-এর ইনভার্ট+১ নিয়মে: +3=0011, ইনভার্ট=1100, +1=1101)

এই দুটো বিট-প্যাটার্ন সরাসরি L09-এর রিপল ক্যারি অ্যাডারে পাঠালে —

$$0101 + 1101 = 1\,0010$$

ফলাফল ৫ বিটের (একটি অতিরিক্ত ক্যারি-আউট বিটসহ) — কিন্তু যেহেতু আমরা কঠোরভাবে 4-বিট রেজিস্টারে কাজ করছি, সবচেয়ে বাম দিকের অতিরিক্ত ক্যারি-আউট বিটটি স্বাভাবিকভাবেই বাতিল (discard) হয়ে যায়, রেখে যায় শুধু 0010 = 2 — যা ঠিক সঠিক উত্তর ($5-3=2$)। CPU-কে "এটা আসলে বিয়োগ ছিল" এটা আলাদাভাবে বুঝতে হয়নি — একই হার্ডওয়্যার দুটো two's complement বিট-প্যাটার্ন সঠিকভাবেই প্রসেস করেছে।

৩ · ওভারফ্লো পুনরায় — $5+5$ একটি genuine কেস

এখন এমন একটি কেস দেখা যাক যেখানে দুটো প্রকাশযোগ্য সংখ্যা যোগ করলেও ফলাফল প্রকাশযোগ্য রেঞ্জের বাইরে চলে যায়। 4-বিট two's complement-এর সর্বোচ্চ ধনাত্মক মান হলো $+7$ (0111) — কিন্তু $5+5=10$, যা এই রেঞ্জের বাইরে। L18-এর ওভারফ্লো-নিয়ম (সাইন-বিটে ঢোকা ক্যারি ≠ সাইন-বিট থেকে বের হওয়া ক্যারি) এই কেসে সঠিকভাবে ওভারফ্লো পতাকা তুলবে কিনা — তা নিচের কোড সেলে সরাসরি সার্কিট-সিমুলেশন দিয়ে যাচাই করা হচ্ছে।

Python
# L09 + L18 সংশ্লেষণ -- বিট-লেভেল রিপল ক্যারি অ্যাডার/সাবট্রাক্টর দিয়ে প্রকৃত সাইনড অ্যারিথমেটিক
# (সিমুলেটেড ফুল-অ্যাডার চেইন -- কোনো বাস্তব CPU ইনস্ট্রাকশন বা হার্ডওয়্যার অ্যাক্সেস নয়)

def to_twos_complement(n, bits):
    if n >= 0:
        return bin(n)[2:].zfill(bits)
    positive_bits = to_twos_complement(-n, bits)
    inverted = "".join("1" if c == "0" else "0" for c in positive_bits)
    return bin((int(inverted, 2) + 1) % (2 ** bits))[2:].zfill(bits)

def from_twos_complement(bits_str):
    n = len(bits_str)
    value = int(bits_str, 2)
    return value - 2 ** n if bits_str[0] == "1" else value

def full_adder(a, b, cin):
    """L08 -- তিন বিট যোগ করে (sum, carry_out) দেয়"""
    s = a ^ b ^ cin
    cout = (a & b) | (cin & (a ^ b))
    return s, cout

def ripple_carry_adder(a_bits, b_bits, cin=0):
    """L09 -- N-টি ফুল অ্যাডার চেইন করে, LSB থেকে MSB পর্যন্ত ক্যারি প্রবাহিত হয়"""
    n = len(a_bits)
    result = ["0"] * n
    carry = cin
    carry_into_msb = None
    for i in range(n - 1, -1, -1):
        if i == 0:
            carry_into_msb = carry
        a, b = int(a_bits[i]), int(b_bits[i])
        s, carry = full_adder(a, b, carry)
        result[i] = str(s)
    carry_out_of_msb = carry
    return "".join(result), carry_into_msb, carry_out_of_msb

def adder_subtractor(a_bits, b_bits, subtract_mode=False):
    """L09 -- subtract_mode=True হলে B ইনভার্ট করে Cin=1 দেয় (two's complement বিয়োগ ট্রিক)"""
    if subtract_mode:
        b_bits = "".join("1" if c == "0" else "0" for c in b_bits)
        cin = 1
    else:
        cin = 0
    return ripple_carry_adder(a_bits, b_bits, cin)

print("=== 5 + (-3), 4-বিট two's complement অ্যাডার দিয়ে ===")
a = to_twos_complement(5, 4)
b = to_twos_complement(-3, 4)
result, cin, cout = ripple_carry_adder(a, b)
print(f"a(5) = {a}   b(-3) = {b}")
print(f"অ্যাডার আউটপুট বিট = {result}   (top-bit carry_out={cout} বাতিল হয়)")
print(f"দশমিক ব্যাখ্যা = {from_twos_complement(result)}")
assert from_twos_complement(result) == 5 + (-3)
print("cross-check (Python native):", 5 + (-3))

print()
print("=== 5 + 5, ওভারফ্লো কেস যাচাই ===")
a = to_twos_complement(5, 4)
b = to_twos_complement(5, 4)
result, cin, cout = ripple_carry_adder(a, b)
overflow = cin != cout
print(f"a(5) = {a}   b(5) = {b}")
print(f"অ্যাডার আউটপুট বিট = {result}   carry_in(sign)={cin}  carry_out(sign)={cout}")
print(f"ওভারফ্লো ফ্ল্যাগ (L18 নিয়ম) = {overflow}")
print(f"(হার্ডওয়্যারের ভুল দশমিক পাঠ, যদি ওভারফ্লো উপেক্ষা করা হতো: {from_twos_complement(result)}, প্রকৃত যোগফল: {5+5})")

print()
print("=== একই adder_subtractor ইউনিট দিয়ে বিয়োগ: 7 - 3 ===")
a = to_twos_complement(7, 4)
b = to_twos_complement(3, 4)
result, cin, cout = adder_subtractor(a, b, subtract_mode=True)
print(f"7 - 3 => বিট {result} => দশমিক {from_twos_complement(result)}")
assert from_twos_complement(result) == 7 - 3

    
কোডের আউটপুট দুটো গুরুত্বপূর্ণ জিনিস প্রমাণ করছে — প্রথমত, 5 + (-3)-এর জন্য কোনো বিশেষ "বিয়োগ মোড" চালু করতে হয়নি, শুধু সঠিক two's complement বিট-প্যাটার্ন পাঠালেই ripple_carry_adder সঠিক উত্তর দিয়েছে। দ্বিতীয়ত, 5 + 5-এ carry_in(sign)=1 কিন্তু carry_out(sign)=0 হওয়ায় overflow=True সঠিকভাবে ধরা পড়েছে — অ্যাডার বিট-প্যাটার্ন সরাসরি পড়লে (ইনভার্ট নিয়মে) ভুল দশমিক মান (-6) বেরিয়ে আসত, তাই ওভারফ্লো ফ্ল্যাগ ছাড়া এই ভুল ধরা পড়ত না — এটাই প্রমাণ করে কেন হার্ডওয়্যারে ওভারফ্লো ফ্ল্যাগ থাকা আবশ্যক।
মূল কথা · Key takeaway

একটি একক ripple-carry adder circuit, সামান্য একটি XOR-নিয়ন্ত্রিত bit-inverter ও mode-select ওয়্যার যোগ করলেই, যোগ ও বিয়োগ — দুটোই সঠিকভাবে চালাতে পারে, দুই ধরনের অপারেশনের জন্য দুই ধরনের সার্কিট বানানোর প্রয়োজন নেই। এই সাশ্রয়ী ডিজাইনটাই real ALU (Arithmetic Logic Unit) হার্ডওয়্যারের একটি মূল ভিত্তি (M6-এ বিস্তারিত)।

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

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

প্র ০১ উপরের কোডে 5 + (-3) গণনায় top-bit-এর carry_out=1 এসেছে, কিন্তু সেটাকে "বাতিল" করা হলো কেন — এটা কি একটা তথ্য হারানো?

না, কারণ আমরা কঠোরভাবে 4-বিট রেজিস্টারে কাজ করছি এবং ফলাফল ($2$) 4-বিটের রেঞ্জের মধ্যেই পুরোপুরি প্রকাশযোগ্য — এই ক্যারি-আউট শুধু একটি "৫ম বিট" যা 4-বিট two's complement রেজিস্টারে জায়গা পায় না, কিন্তু যেহেতু প্রকৃত গাণিতিক উত্তর রেঞ্জের মধ্যেই আছে, এটি বাতিল করলে কোনো ভুল হয় না। L18-এর ওভারফ্লো-নিয়ম অনুযায়ী, শুধু তখনই সমস্যা হবে যখন carry-in আর carry-out আলাদা হয় (যেমন 5+5 কেসে) — 5+(-3) কেসে তারা একই (উভয়ই 1), তাই কোনো ওভারফ্লো ঘটেনি, ক্যারি-আউট নিরাপদে বাতিলযোগ্য।

প্র ০২ যদি একটি সরল অ্যাডার (সাবট্রাক্টর-লজিক ছাড়া) দিয়ে সরাসরি $7$ ও $-3$ যোগ করা হয়, তাহলে কি "বিয়োগ মোড" চালু করা লাগবে?

না — এটাই এই পাঠের মূল বিন্দু। $-3$ ইতিমধ্যে two's complement বিট-প্যাটার্নে এনকোড করা হয়ে গেছে (to_twos_complement(-3, 4)), তাই এটাকে সরাসরি সাধারণ অ্যাডারে (ripple_carry_adder, কোনো subtract_mode ছাড়াই) পাঠালেই সঠিক ফলাফল আসে। "সাবট্রাক্টর মোড" (B ইনভার্ট + Cin=1) শুধু তখনই দরকার যখন ইনপুট দুটো ধনাত্মক বিট-প্যাটার্ন থাকে এবং আমরা একটিকে আরেকটি থেকে বিয়োগ করতে চাই (যেমন কোড সেলের শেষ উদাহরণ 7 - 3) — সেখানে B আগে থেকে ঋণাত্মক এনকোড করা ছিল না।

প্র ০৩ উপরের 5 + 5 ওভারফ্লো কেসে অ্যাডার আউটপুট বিট 1010 সরাসরি two's complement হিসেবে পড়লে কী মান পাওয়া যায়, আর সেটা কেন "ভুল"?

1010-কে সরাসরি 4-বিট two's complement হিসেবে পড়লে পাওয়া যায় $1\times(-8)+0\times4+1\times2+0\times1 = -6$। এটা স্পষ্টতই ভুল, কারণ প্রকৃত গাণিতিক যোগফল $5+5=10$, $-6$ নয়। সমস্যাটা হলো $10$ 4-বিট signed রেঞ্জ $[-8,7]$-এর বাইরে, তাই এটি সেই রেঞ্জের মধ্যে "wrap around" করে একটি সম্পূর্ণ ভুল কিন্তু বৈধ-দেখতে বিট-প্যাটার্নে পরিণত হয়েছে — এই কারণেই ওভারফ্লো ফ্ল্যাগ (carry_in ≠ carry_out) আলাদাভাবে চেক করা বাধ্যতামূলক; নইলে হার্ডওয়্যার নীরবে একটি সম্পূর্ণ ভুল উত্তর দিয়ে দিত।

অনুশীলন

  1. হাতে-কলমে করুন: 4-বিট two's complement ব্যবহার করে $-4 + (-3)$ বিট-লেভেলে যোগ করুন (উভয় সংখ্যাকে আগে two's complement-এ রূপান্তর করুন), তারপর যাচাই করুন এটি ওভারফ্লো ঘটাচ্ছে কিনা।

    $-4 \to 1100$, $-3 \to 1101$। যোগ: $1100+1101 = 1\,1001$, top-bit বাতিল করে $1001$। যাচাই: $1\times(-8)+0+0+1 = -7$ — সঠিক ($-4-3=-7$)। carry_in(sign)=1, carry_out(sign)=1 — উভয়ই সমান, তাই ওভারফ্লো নেই, এবং $-7$ 4-বিট রেঞ্জ $[-8,7]$-এর মধ্যেই আছে, যা সামঞ্জস্যপূর্ণ।

  2. চিন্তা করুন: $(-8) + (-1)$ 4-বিট two's complement-এ যোগ করলে কি ওভারফ্লো ঘটবে? আগে অনুমান করুন, তারপর কারণ ব্যাখ্যা করুন।

    হ্যাঁ, ওভারফ্লো ঘটবে। $-8+(-1)=-9$, যা 4-বিট signed রেঞ্জ $[-8,7]$-এর বাইরে (সবচেয়ে ঋণাত্মক প্রকাশযোগ্য মান $-8$)। বিট-লেভেলে: $-8\to1000$, $-1\to1111$, যোগ $1000+1111=1\,0111$, top-bit বাতিল করলে $0111=+7$ — একটি স্পষ্টভাবে ভুল, উল্টো-চিহ্নের ফলাফল, যা ঠিক ওভারফ্লোর ক্লাসিক লক্ষণ। carry_in(sign) ও carry_out(sign) চেক করলে তারা আলাদা হবে, ফ্ল্যাগ সঠিকভাবে সমস্যাটি ধরবে।

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

আগের পাঠ
সাইনড নাম্বার রিপ্রেজেন্টেশন — Two's Complement