কম্পিউটারে সংখ্যা উপস্থাপন — ফ্লোটিং পয়েন্ট
এই পাঠে যা শিখবেন
- কম্পিউটার কীভাবে বাইনারিতে দশমিক সংখ্যা সঞ্চয় করে এবং কেন এটি একটি আনুমানিক প্রক্রিয়া
- IEEE 754 ডাবল-প্রিসিশন ফরম্যাটের মূল কাঠামো (সাইন, এক্সপোনেন্ট, ম্যান্টিসা)
- মেশিন এপসিলন কী এবং কীভাবে এটি নির্ভুলতার সীমা নির্ধারণ করে
- একটি সত্যিকারের Python ডেমো —
0.1 + 0.2 != 0.3,repr()দিয়ে প্রকৃত পার্থক্য দেখা, এবং বড় সংখ্যায় নির্ভুলতা হারানোর সরাসরি প্রমাণ
১ · কেন কম্পিউটার "দশমিক" সংখ্যা সঠিকভাবে সঞ্চয় করতে পারে না
আমরা মানুষ দশমিক (১০-ভিত্তিক) সংখ্যা পদ্ধতিতে চিন্তা করি, কিন্তু কম্পিউটার ভেতরে সবকিছু
বাইনারিবাইনারি (Binary)২-ভিত্তিক সংখ্যা পদ্ধতি — প্রতিটি অঙ্ক শুধু ০ অথবা ১ হতে পারে। কম্পিউটারের সব হার্ডওয়্যার এই পদ্ধতিতে কাজ করে।
(২-ভিত্তিক) আকারে সঞ্চয় করে। সমস্যা হলো, দশমিকে যে ভগ্নাংশ সসীম অঙ্কে সঠিকভাবে লেখা যায়
(যেমন 0.1), তা বাইনারিতে প্রায়ই অসীম পুনরাবৃত্ত হয়ে যায় — ঠিক যেমন দশমিকে
1/3 = 0.333... কখনো শেষ হয় না। 0.1-কে বাইনারিতে লিখলে পাওয়া যায়
0.0001100110011... — একটি অসীম পুনরাবৃত্ত প্যাটার্ন। যেহেতু কম্পিউটারের মেমরি সসীম
(৬৪ বিট), এই সংখ্যাটিকে কোনো এক পর্যায়ে কেটে ফেলতে (truncate) হয় — আর সেখান থেকেই
সব ফ্লোটিং-পয়েন্ট ভুলের শুরু।
২ · IEEE 754 — আধুনিক ফ্লোটিং-পয়েন্ট স্ট্যান্ডার্ড
Python (এবং প্রায় প্রতিটি আধুনিক ভাষা)-এর float টাইপ
IEEE 754 ডাবল-প্রিসিশনIEEE 754ফ্লোটিং-পয়েন্ট সংখ্যা সঞ্চয় ও গণনার জন্য আন্তর্জাতিক স্ট্যান্ডার্ড। "ডাবল-প্রিসিশন" মানে ৬৪ বিট ব্যবহার করা হয় (সিঙ্গল-প্রিসিশন ৩২ বিট ব্যবহার করে, কম নির্ভুল)।
ফরম্যাট মেনে চলে — মোট ৬৪ বিট, যা তিনটি ভাগে বিভক্ত:
সংখ্যাটি ধনাত্মক না ঋণাত্মক তা নির্ধারণ করে।
সংখ্যাটি কতটা বড় বা ছোট (দশমিক বিন্দু কোথায় বসবে) তা নির্ধারণ করে — এর ফলে অত্যন্ত ছোট (
~10⁻³⁰⁸) থেকে অত্যন্ত বড় (~10³⁰⁸) সংখ্যা প্রকাশ করা যায়।প্রকৃত সিগনিফিক্যান্ট অঙ্কগুলো সঞ্চয় করে — এটাই নির্ভুলতা নির্ধারণ করে, প্রায় ১৫-১৭ দশমিক অঙ্কের সমতুল্য।
মূল কথা: ৫২ বিট ম্যান্টিসার মানে সসীম নির্ভুলতা — প্রতিটি ফ্লোটিং-পয়েন্ট সংখ্যা আসলে একটি আনুমানিক মান, ঠিক মান নয় (কিছু বিশেষ ক্ষেত্র বাদে, যেমন ছোট পূর্ণসংখ্যা বা $2$-এর ঘাত)।
৩ · সত্যিকারের ডেমো — 0.1 + 0.2 == 0.3 কেন False
নিচের কোড সেলে সত্যিই Python চালিয়ে দেখানো হয়েছে 0.1 ও 0.2 যোগ করলে ঠিক
0.3 পাওয়া যায় না — এবং repr() দিয়ে প্রকৃত, লুকানো পার্থক্যটি দেখা যায়।
import sys
a = 0.1 + 0.2
b = 0.3
print("0.1 + 0.2 =", a)
print("0.3 =", b)
print("0.1 + 0.2 == 0.3 ->", a == b)
print("repr(0.1+0.2) =", repr(a))
print("repr(0.3) =", repr(b))
print("প্রকৃত পার্থক্য =", repr(a - b))
print(f"a-কে ২০ দশমিক স্থান পর্যন্ত: {a:.20f}")
print(f"b-কে ২০ দশমিক স্থান পর্যন্ত: {b:.20f}")
print()
print("মেশিন এপসিলন (sys.float_info.epsilon) =", sys.float_info.epsilon)
print("এক ফ্লোটে সিগনিফিক্যান্ট দশমিক অঙ্ক (dig) =", sys.float_info.dig)
0.1 + 0.2 = 0.30000000000000004 — অর্থাৎ 0.3-এর চেয়ে
সামান্য বেশি। repr() দিয়ে দেখা যায় প্রকৃত পার্থক্য মাত্র
5.551115123125783e-17 — অর্থাৎ দশমিকের পর ১৬টি শূন্যের পরে একটি অঙ্ক।
এটি এতটাই ছোট যে বেশিরভাগ বাস্তব হিসাবে কোনো সমস্যা করে না, কিন্তু == দিয়ে সরাসরি তুলনা
করলে এটি False ফেরত দেয় — ফ্লোটিং-পয়েন্ট সংখ্যা তুলনা করার সময় এটি একটি সাধারণ,
বিপজ্জনক ভুলের উৎস। মেশিন এপসিলন 2.220446049250313e-16 — অর্থাৎ 1.0-এর
কাছাকাছি সংখ্যায় নির্ভুলতার সীমা প্রায় এই মাত্রার।
৪ · বড় সংখ্যায় নির্ভুলতা হারানো
ম্যান্টিসার ৫২ বিট একটি আপেক্ষিক (relative) নির্ভুলতা দেয়, পরম (absolute) নয় — তাই সংখ্যা যত বড় হয়, দুটি পাশাপাশি প্রকাশযোগ্য ফ্লোটের মধ্যে দূরত্বও তত বড় হয়। নিচের কোড সেলে এটি সরাসরি দেখানো হয়েছে।
x = 1e16
print("x =", x)
print("x + 1 == x ->", x + 1 == x)
print("x + 1 =", x + 1)
print()
y = 100.0
print("y =", y)
print("y + 1 == y ->", y + 1 == y)
print("y + 1 =", y + 1)
1e16 + 1 == 1e16 ফেরত দেয় True — অর্থাৎ 1 যোগ করার
পরও সংখ্যাটি একই থেকে যায়, কারণ 10¹⁶ মাত্রায় পাশাপাশি দুটি ফ্লোট-প্রকাশযোগ্য সংখ্যার মধ্যে
ব্যবধান ইতিমধ্যে 1-এর চেয়ে বড়, তাই +1 সম্পূর্ণ "হারিয়ে যায়"। অথচ
y = 100.0-এর ক্ষেত্রে y + 1 == y হয় False — কারণ এই মাত্রায়
1 এখনও প্রকাশযোগ্য নির্ভুলতার সীমার ভেতরে। এই ঘটনাটিকেই "catastrophic
cancellation"-এর একটি চেহারা বলা যায়, এবং L03-এ আমরা দেখব কীভাবে এই ছোট ছোট ভুল
লক্ষ লক্ষ বার পুনরাবৃত্ত হলে একটি বড়, লক্ষণীয় ভুলে পরিণত হতে পারে।
ফ্লোটিং-পয়েন্ট সংখ্যা সীমাহীন নির্ভুল দশমিক নয় — এটি একটি সসীম-বিট আনুমানিক প্রকাশ,
যার নিজস্ব, গণনাযোগ্য নির্ভুলতার সীমা (মেশিন এপসিলন) আছে। এই সীমা বোঝা এই পুরো কোর্সের ভিত্তি —
কারণ প্রতিটি নিউমেরিক্যাল অ্যালগরিদম শেষ পর্যন্ত এই একই সীমিত-নির্ভুলতার সংখ্যা দিয়েই কাজ করে।
../math-for-ai/index.html-এ সংখ্যা ও সেট নিয়ে গাণিতিক ভিত্তি দেওয়া আছে; এই পাঠ সেই
গাণিতিক বস্তু কম্পিউটারে বাস্তবে কীভাবে সঞ্চিত হয় তা দেখাল।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
0.1 + 0.2 == 0.3 কেন সরাসরি False — অথচ 0.1 + 0.1 == 0.2
চেক করলে Python True ফেরত দেয়? এর মানে কি বাইনারিতে 0.1 কখনো কখনো সঠিক?
না — 0.1 বাইনারিতে কখনোই সঠিকভাবে প্রকাশযোগ্য নয়, তাই দুটি ক্ষেত্রেই কিছু রাউন্ড-অফ
ভুল থাকে। তবে 0.1 + 0.1-এর ক্ষেত্রে ভুলগুলো এমনভাবে মিলে যায় (দ্বিগুণ করার সময় শুধু
এক্সপোনেন্ট এক ধাপ বাড়ে, ম্যান্টিসার বিটপ্যাটার্ন অপরিবর্তিত থাকে) যে চূড়ান্ত রাউন্ড-অফ করা মান
একই ভুলযুক্ত 0.2-এর প্রকাশের সাথে মিলে যায়। 0.1 + 0.2-এ দুটি ভিন্ন
বিটপ্যাটার্ন যোগ হয়, এবং সেই যোগের রাউন্ড-অফ ভুল 0.3-এর নিজস্ব রাউন্ড-অফ ভুলের সাথে
মেলে না। এটাই দেখায় কেন ফ্লোটিং-পয়েন্ট আচরণ মাঝে মাঝে অনির্দেশ্য মনে হয় — আসলে এটি সম্পূর্ণ
নিয়ন্ত্রিত, কিন্তু বাইনারি স্তরে বিশ্লেষণ না করলে অনুমান করা কঠিন।
প্র ০২
1e16 + 1 == 1e16 ডেমোতে দেখা গেল বড় সংখ্যায় +1 সম্পূর্ণ হারিয়ে যায়।
এটি কোন ধরনের বাস্তব প্রোগ্রামে বিপজ্জনক হতে পারে বলে আপনার মনে হয়?
যেকোনো জায়গায় যেখানে একটি বড় সংখ্যায় বারবার ছোট মান যোগ করা হয় — যেমন একটি লুপে অনেকবার একটি ছোট
কাউন্টার বা টাইম-স্টেপ যোগ করে একটি ক্রমবর্ধমান "running total" রাখা, বিশেষত সিমুলেশন বা আর্থিক
হিসাবে যেখানে লক্ষ লক্ষ ছোট লেনদেন একটি বড় ব্যালেন্সে যোগ হয়। যদি সেই ব্যালেন্স যথেষ্ট বড় হয়ে যায়,
পরবর্তী ছোট যোগগুলো নীরবে হারিয়ে যেতে পারে — কোনো এরর ছাড়াই। এই কারণেই আর্থিক সিস্টেমে প্রায়ই
ফ্লোট এড়িয়ে পূর্ণসংখ্যা (যেমন সেন্ট গণনা) বা decimal মডিউল ব্যবহার করা হয়।
প্র ০৩
মেশিন এপসিলন 1.0-এর কাছে প্রায় 2.22 × 10⁻¹⁶। আপনার কী মনে হয়, এই মানটি
1000.0-এর কাছেও ঠিক একই থাকবে, নাকি বদলাবে? কেন?
এটি বদলাবে — বাড়বে। মেশিন এপসিলন একটি আপেক্ষিক নির্ভুলতার পরিমাপ, পরম নয়। যেহেতু
এক্সপোনেন্ট অংশ সংখ্যার মাত্রা অনুযায়ী বদলায়, 1000.0-এর কাছাকাছি দুটি পাশাপাশি
প্রকাশযোগ্য ফ্লোটের মধ্যে পরম দূরত্ব 1.0-এর কাছাকাছি দূরত্বের চেয়ে প্রায় ১০০০ গুণ বড়
হবে (কারণ ম্যান্টিসার বিট-সংখ্যা স্থির থাকে, কিন্তু সংখ্যার মাত্রা বাড়ে) — এটাই ঠিক আগের ডেমোতে
দেখানো 1e16 + 1 == 1e16 ঘটনার পেছনের মূল কারণ।
অনুশীলন
-
চিন্তা করুন: উপরের প্রথম কোড সেলে
a - b-এর বদলেb - aগণনা করলে ফলাফলের চিহ্ন (sign) কী হবে, এবং পরম মান কি একই থাকবে বলে আপনার ধারণা?পরম মান একই থাকবে (
5.551115123125783e-17), শুধু চিহ্ন উল্টে যাবে — অর্থাৎb - aহবে ঠিক-5.551115123125783e-17। কারণ এটি স্রেফ বিয়োগ, এবং ফ্লোটিং-পয়েন্ট বিয়োগ পাটিগণিতের এই মৌলিক নিয়ম মেনে চলে। -
পরীক্ষা করুন: দ্বিতীয় কোড সেলে
x = 1e16-কেx = 1e15-এ পরিবর্তন করে Run চেপে দেখুনx + 1 == xএখনও True থাকে কিনা।x = 1e15-এx + 1 == xহয় False — অর্থাৎ1e15মাত্রায়+1এখনও প্রকাশযোগ্য নির্ভুলতার সীমার ভেতরে থাকে, কিন্তু1e16মাত্রায় গিয়ে সেই সীমা পেরিয়ে যায়। এটি দেখায় IEEE 754 ডাবল-প্রিসিশনে "১-এর নির্ভুলতা হারানোর সীমা" প্রায়2⁵³ ≈ 9 × 10¹⁵-এর কাছাকাছি — যা ৫২ বিট ম্যান্টিসার সরাসরি ফল।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ — এররের উৎস L03 রাউন্ড-অফ এরর কীভাবে বারবার পুনরাবৃত্ত হয়ে জমা হয়, এবং ট্রাংকেশন এরর কী — এই পাঠে শেখা ফ্লোটিং-পয়েন্ট ভিত্তির উপর দাঁড়িয়ে।
- Math for AI & ML কোর্স সহোদর কোর্স সংখ্যা পদ্ধতি ও গাণিতিক ভিত্তি নিয়ে আরও গভীরে জানতে।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ এরর অ্যানালাইসিস, রুট-ফাইন্ডিং, লিনিয়ার সিস্টেম, ইন্টারপোলেশন, নিউমেরিক্যাল ইন্টিগ্রেশন, ODE সলভিং, আইগেনভ্যালু মেথড, অপ্টিমাইজেশন ও ক্যাপস্টোন।