পাঠ ২০ · ৫৭-এর মধ্যে · মডিউল ৪

বাইনারি মাল্টিপ্লিকেশন — Booth's Algorithm

Binary multiplication — Booth's algorithm
৯ মিনিট পড়া মধ্যম-উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

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

  • নাইভ শিফট-অ্যান্ড-অ্যাড গুণ পদ্ধতি বেসলাইন হিসেবে বোঝা
  • Booth's Algorithm-এর ঠিক-নিয়মে বিট-পেয়ার রিকোডিং — "01"→add, "10"→subtract, "00"/"11"→no-op
  • $3 \times (-2)$-এর প্রতিটি ৪টি iteration হাতে-নয়, কোডের প্রকৃত স্টেপ-বাই-স্টেপ প্রিন্ট আউটপুট দিয়ে সাবধানে ট্রেস করা
  • L18-L19-এর two's complement conversion ও adder/subtractor পুনর্ব্যবহার করে Booth's-কে বিট-লেভেলে বাস্তবায়ন করা

১ · নাইভ বাইনারি গুণ — বেসলাইন

সবচেয়ে সরল বাইনারি গুণ পদ্ধতি হাতে-করা লম্বা গুণের মতোই — গুণকের (multiplier) প্রতিটি ১-বিটের জন্য, সেই বিটের পজিশন অনুযায়ী গুণ্যকে (multiplicand) বাম দিকে শিফট করে একটি running total-এ যোগ করা হয়। N-বিট সংখ্যার জন্য এতে সর্বোচ্চ N-টি পার্শিয়াল-প্রোডাক্ট যোগ লাগতে পারে — সঠিক, কিন্তু ধারাবাহিক ১-এর রান (যেমন 0111) থাকলে অপ্রয়োজনীয়ভাবে অনেকগুলো যোগ করতে হয়।

২ · Booth's Algorithm — স্মার্ট রিকোডিং

Booth's AlgorithmBooth's Algorithmগুণকের পাশাপাশি দুটি বিট পরীক্ষা করে প্রতিটি ধাপে যোগ/বিয়োগ/কোনো-অপারেশন-নয় — এই তিনটির একটি বেছে নেয়, তারপর পুরো ওয়ার্কিং রেজিস্টার এক বিট ডানে শিফট করে। প্রতিটি ধাপে গুণকের বর্তমান বিট ও তার ঠিক ডানপাশের বিট (শুরুতে একটি অন্তর্নিহিত ০ ধরে নিয়ে) — এই জোড়া পরীক্ষা করে সিদ্ধান্ত নেয় —

bit-pair "01"
multiplicand যোগ করো (A = A + M)
bit-pair "10"
multiplicand বিয়োগ করো (A = A − M)
bit-pair "00" বা "11"
কোনো অ্যারিথমেটিক নয় — শুধু শিফট

প্রতিটি ধাপের অ্যারিথমেটিক অ্যাকশনের পর, সম্পূর্ণ ওয়ার্কিং রেজিস্টার — অ্যাকুমুলেটর (A) + গুণকের বিট (Q) + Booth's-এর অতিরিক্ত বিট ($Q_{-1}$) — একসাথে এক বিট ডানে arithmetic shift হয় (সাইন বিট সংরক্ষণ করে)। এই "ধারাবাহিক ১-এর রান এড়িয়ে যাওয়া" ক্ষমতাই Booth's-কে নাইভ পদ্ধতির চেয়ে দ্রুত করে তোলে।

৩ · ওয়ার্কড উদাহরণ — $3 \times (-2)$, প্রত্যাশিত ফলাফল $-6$

এই অ্যালগরিদম হাতে-ট্রেস করা genuinely ভুল-প্রবণ — তাই এখানে হাত দিয়ে ফলাফল না বসিয়ে, নিচের কোড সেলের প্রকৃত স্টেপ-বাই-স্টেপ প্রিন্ট আউটপুটের উপর নির্ভর করা হচ্ছে। 4-বিট two's complement-এ $M=3\to 0011$ (multiplicand), $Q=-2\to 1110$ (multiplier), $A=0000$ (অ্যাকুমুলেটর, শুরুতে শূন্য), $Q_{-1}=0$।

Python
# Booth's Algorithm -- বিট-লেভেল সিমুলেশন, L18 conversion ও L19 adder/subtractor পুনর্ব্যবহার করে
# (কোনো বাস্তব CPU ইনস্ট্রাকশন নয় -- সম্পূর্ণ toy বিট-স্ট্রিং সিমুলেশন)

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):
    s = a ^ b ^ cin
    cout = (a & b) | (cin & (a ^ b))
    return s, cout

def ripple_carry_adder(a_bits, b_bits, cin=0):
    n = len(a_bits)
    result = ["0"] * n
    carry = cin
    for i in range(n - 1, -1, -1):
        a, b = int(a_bits[i]), int(b_bits[i])
        s, carry = full_adder(a, b, carry)
        result[i] = str(s)
    return "".join(result), carry

def adder_subtractor(a_bits, b_bits, subtract_mode=False):
    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)

def booth_multiply(multiplicand, multiplier, bits):
    """Booth's Algorithm -- প্রতিটি ধাপের বিট-পেয়ার, action ও intermediate অবস্থা ট্রেস করে রিটার্ন করে"""
    M = to_twos_complement(multiplicand, bits)
    Q = to_twos_complement(multiplier, bits)
    A = "0" * bits
    Q_minus1 = "0"
    trace = []
    for step in range(1, bits + 1):
        pair = Q[-1] + Q_minus1
        if pair == "10":
            action = "A = A - M (subtract)"
            A, _ = adder_subtractor(A, M, subtract_mode=True)
        elif pair == "01":
            action = "A = A + M (add)"
            A, _ = adder_subtractor(A, M, subtract_mode=False)
        else:
            action = "no-op (00/11), শুধু শিফট"
        a_after_op = A
        # A : Q : Q_minus1 একত্রে arithmetic right shift (সাইন বিট সংরক্ষণ করে)
        combined = A + Q + Q_minus1
        sign_bit = combined[0]
        shifted = sign_bit + combined[:-1]
        A, Q, Q_minus1 = shifted[:bits], shifted[bits:2 * bits], shifted[-1]
        trace.append((step, pair, action, a_after_op, A, Q, Q_minus1))
    return A + Q, trace

product_bits, trace = booth_multiply(3, -2, 4)

print("M (multiplicand=3) =", to_twos_complement(3, 4))
print("Q (multiplier=-2)  =", to_twos_complement(-2, 4))
print()
for step, pair, action, a_after_op, A, Q, Qm1 in trace:
    print(f"ধাপ {step}: বিট-পেয়ার(Q0,Q-1)={pair}  action={action}")
    print(f"        অ্যারিথমেটিকের পর A={a_after_op}   শিফটের পর A={A} Q={Q} Q-1={Qm1}")

final_decimal = from_twos_complement(product_bits)
print()
print(f"চূড়ান্ত প্রোডাক্ট বিট (A:Q) = {product_bits}  ->  দশমিক = {final_decimal}")
print("cross-check (Python native 3 * -2):", 3 * -2)
assert final_decimal == 3 * -2
print("MATCH -- Booth's Algorithm সঠিকভাবে -6 দিয়েছে")

    
হাতে-যাচাই করা যাক ধাপ ২: এই ধাপে $Q_0=1$, $Q_{-1}=0$ ছিল (আগের ধাপের শিফটের পর) — pair "10" মানে বিয়োগ। কোডের আউটপুটে ঠিক এই ধাপেই "A = A - M (subtract)" অ্যাকশন দেখা যাচ্ছে। বাকি ধাপগুলোতে (১, ৩, ৪) pair "00" বা "11" — অর্থাৎ কোনো অ্যারিথমেটিক ছাড়াই শুধু শিফট হয়েছে, ঠিক এইজন্যই মাত্র একটি অ্যারিথমেটিক অপারেশনেই (৪-বিট সংখ্যার জন্য সম্ভাব্য ৪টির বদলে) পুরো গুণ সম্পন্ন হয়েছে — এটাই Booth's-এর দক্ষতার প্রকৃত উদাহরণ।
মূল কথা · Key takeaway

Booth's Algorithm কোনো নতুন গাণিতিক সত্য আবিষ্কার করে না — এটি শুধু L19-এর একই adder/subtractor হার্ডওয়্যার পুনর্ব্যবহার করে, কিন্তু স্মার্টভাবে সিদ্ধান্ত নেয় ঠিক কখন সেটি ব্যবহার করতে হবে (আর কখন এড়িয়ে যাওয়া যায়) — একটি সুন্দর উদাহরণ কীভাবে অ্যালগরিদমিক চতুরতা হার্ডওয়্যারের কাজের চাপ কমাতে পারে, শুধু বেশি সার্কিট যোগ না করেই।

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

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

প্র ০১ উপরের ট্রেসে ধাপ ১-এ pair "00" কেন পাওয়া গেল, যদিও $Q$-এর সর্বশেষ বিট (LSB, $Q_0$) আসলে ০ ছিল?

$Q=-2$-এর 4-বিট two's complement হলো 1110 — এর সর্বশেষ (রাইটমোস্ট) বিট $Q_0=0$। আর $Q_{-1}$ শুরুতে সংজ্ঞা অনুযায়ী ০ (একটি অন্তর্নিহিত অতিরিক্ত বিট)। তাই pair $(Q_0,Q_{-1}) = (0,0) = $ "00" — কোনো অ্যারিথমেটিক ছাড়াই শুধু শিফট। এটি প্রত্যাশিতই, কারণ Booth's-এর নিয়ম অনুযায়ী যখন উভয় বিট একই (00 বা 11) তখন সেটি মানে গুণকের ওই অংশে কোনো "1-এর রান শুরু বা শেষ" হচ্ছে না।

প্র ০২ যদি এই একই গুণ নাইভ শিফট-অ্যান্ড-অ্যাড পদ্ধতিতে করা হতো ($Q=-2=1110$-এর প্রতিটি ১-বিটের জন্য একটি যোগ), তাহলে কয়টি যোগ লাগত?

$1110$-এ তিনটি ১-বিট আছে (বিট পজিশন ৩, ২, ১) — নাইভ পদ্ধতিতে প্রতিটির জন্য একটি করে শিফট-করা multiplicand যোগ করতে হতো, অর্থাৎ ৩টি পার্শিয়াল-প্রোডাক্ট যোগ। অথচ Booth's Algorithm ধারাবাহিক ১-এর রান (বিট ৩-১, "111")-কে একটি একক বিয়োগ (শুরুতে) ও একটি (অন্তর্নিহিত) যোগ-এর সমতুল্য জোড়ায় রূপান্তরিত করেছে — কোডের ট্রেস অনুযায়ী মাত্র একটি প্রকৃত অ্যারিথমেটিক অপারেশনেই (ধাপ ২-এর বিয়োগ) কাজ শেষ হয়েছে, দেখাচ্ছে কীভাবে Booth's রান-লেংথ-স্টাইল অপ্টিমাইজেশন যোগের সংখ্যা কমায়।

প্র ০৩ কোডে "arithmetic shift" শব্দটি ব্যবহার করা হয়েছে, সাধারণ "logical shift" নয় কেন — পার্থক্য কী?

লজিক্যাল শিফটে ডান দিকে শিফট করার সময় সবচেয়ে বাম পজিশনে সবসময় ০ ঢোকানো হয় — যা unsigned সংখ্যার জন্য ঠিক আছে। কিন্তু Booth's Algorithm two's complement সাইনড সংখ্যা নিয়ে কাজ করে, যেখানে সবচেয়ে বাম বিট (সাইন বিট)-এর একটি বিশেষ অর্থ আছে ($-2^{N-1}$)। আর্থমেটিক শিফটে ডানে শিফট করার সময় সবচেয়ে বাম পজিশনে নতুন বিট হিসেবে মূল সাইন বিটের একটি কপি ঢোকানো হয় (কোডে sign_bit + combined[:-1]), যাতে সংখ্যাটির চিহ্ন (ধনাত্মক/ঋণাত্মক) শিফটের পরেও ঠিক থাকে — এটি সাইনড অ্যারিথমেটিকের জন্য বাধ্যতামূলক।

অনুশীলন

  1. অনুমান করুন, তারপর যাচাই করুন: $2 \times 3$ (উভয়ই ধনাত্মক) 4-বিট Booth's Algorithm-এ কয়টি ধাপে কতগুলো অ্যারিথমেটিক অপারেশন লাগবে বলে মনে করেন? কোড সেলের ফাংশন কল বদলে (booth_multiply(2, 3, 4)) নিজে যাচাই করুন।

    $Q=3=0011$ — বিট-পেয়ার সিকোয়েন্স বিশ্লেষণ করলে দুটি ট্রানজিশন পাওয়া যাবে (০→১ ও ১→০-এর প্রান্তে) — অর্থাৎ ৪টি ধাপের মধ্যে ২টিতে অ্যারিথমেটিক অপারেশন (একটি add, একটি subtract) ঘটবে, বাকি ২টি শুধু শিফট। চূড়ান্ত ফলাফল অবশ্যই $6$ হওয়া উচিত ($2\times3$) — কোড চালিয়ে final_decimal == 6 নিশ্চিত করে যাচাই করুন।

  2. চিন্তা করুন: Booth's Algorithm-এ multiplicand ($M$) সবসময় একই বিট-প্যাটার্নে (to_twos_complement দিয়ে একবার) থাকে, কিন্তু $A$, $Q$, $Q_{-1}$ প্রতি ধাপে বদলায় — কেন $M$ বদলানোর দরকার নেই?

    $M$ (multiplicand) সম্পূর্ণ অ্যালগরিদম জুড়ে ধ্রুবক থাকে — প্রতিটি ধাপে শুধু সিদ্ধান্ত নেওয়া হয় $M$-কে $A$-তে যোগ করা হবে, বিয়োগ করা হবে, নাকি কিছুই করা হবে না। যেটা বদলায় তা হলো "ওয়ার্কিং স্টেট" ($A$, $Q$, $Q_{-1}$-এর সম্মিলিত বিট-প্যাটার্ন), যা প্রতি ধাপে শিফট হয়ে ধীরে ধীরে চূড়ান্ত প্রোডাক্ট তৈরি করে। এটি অনেকটা লম্বা গুণে গুণ্য সংখ্যাটি একই থাকে, শুধু কোন পজিশনে সেটি যোগ হচ্ছে তা বদলানোর মতো।

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

আগের পাঠ
বাইনারি অ্যাডিশন ও সাবট্রাকশন সার্কিট