یکی از سالمترین سرگرمیهای هر برنامهنویس Backendی فحش دادن به جاوااسکریپت است و پریروز در پایان ساعت کاری همین بحث شیرین در شرکت در جریان بود. از میان ویژگیهای داغان بیشمار این زبان زیبا و دوستداشتنی، نوبت به این نکته رسید که جمع یک دهم و دو دهم در جاوااسکریپت، سه دهم نیست! شاید باورش برایتان سخت باشد اما این اتفاق کاملا درست و منطقی است!
بله! حاصل جمع ۰.۱ و ۰.۲ برابر با ۰.۳ نیست و این گزاره کاملا درست و منطقی است! :))).
بله آقا! بله! منطقی است! :)). ماجرا به نحوهی پردازش و ذخیرهسازی اعداد در کامپیوتر بر میگردد. تکلیف اعداد طبیعی مثل ۴ و ۰ و ۱۲ روشن است، مستقیم نمایش دودویی آنها ذخیره میشود و به راحتی مقدار اصلی نیز بازیابی میشود. اعداد منفی نیز به همین شیوه و گرفتن مکمل۲ ذخیره میشوند. اما برای اعداد اعشاری که از دو قسمت صحیح و اعشار تشکیل شده است چه میتوان کرد؟ چگونه ۱۲۳.۴۵۶۷۸۹ را ذخیره کنیم؟
پاسخ اول به این سوال یک راهحل ساده است! محل ذخیره را سه قسمت کن! قسمت اول که علامت عدد را نشان میدهد (صفر یعنی عدد ذخیرهشده مثبت است و یک یعنی منفی است)، قسمت دوم را برای ذخیرهسازی قسمت صحیح عدد اعشاری استفاده کن و قسمت سوم را برای ذخیرهسازی اعشار آن. یعنی اگر ۸ بیت فضا به این شکل تقسیم شود که ۳ بیت برای قسمت صحیح عدد و ۴ بیت برای ذخیرهی اعشار، در اینصورت بزرگترین عدد اعشاریای که با این روش میتوان ذخیره کرد ۸.۲۳ است! به بیان دیگر این روش همیشه یک پنجره در دست دارد که هر رقمی که بیرون از این پنجره قرار بگیرد را میبُرَد و بدور میریزد!
به روش قبل، روش ممیز ثابت (fixed point) برای ذخیرهسازی اعداد اعشاری میگویند.
راهحل دوم اما اهل مدارای بیشتری است! ایدهی کلی این است که قبل از ذخیرهی عدد اعشاری، آنرا نرمال کنیم و بصورت نمایش علمی بنویسیم. مثلا نمایش علمی ۱۱.۵ میشود ۱.۱۵ در ده به توان ۱، به معنای دیگر، بجای آنکه پنجره بدست همه را با یک چشم بسنجیم، اول آنها را در شرایط نرمال قرار میدهیم و سپس آنها را ذخیره میکنیم! به این روش، روش ممیز شناور میگویند.
طبق تصویر بالا میخواهیم عدد ۰.۱ را ذخیره کنیم. ابتدا آنرا به صورت دودوییاش تبدیل میکنیم و سپس آنرا نرمال میکنیم. متناظر با هر رنگ جدول زیر را در نظر میگیریم:
از سمت راست، رنگ طلایی متناظر با ارقام طلایی است، به این ارقام، ارقام بامعنا میگوئیم. منفی چهار در نمایش نرمال ۰.۱ را بصورت مکمل دو در قسمت سبزرنگ ذخیره میکنیم و در نهایت اون آبی روشن خوشگله هم نمایانگر مثبت یا منفی بودن عدد است!
تا اینجا دیدیم که چگونه یک عدد ناصحیح را میتوان در کامپیوتر ذخیره کرد. به عنوان پست بر میگردیم و در نمایش باینری، جمع را انجام میدهیم:
به نظر شما نمایش دهدهی عدد سبز رنگ چه مقداری است؟ :))). دقیقا! ۰.۳۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۴ :)).
نه! یک قضیه در آنالیز ریاضی وجود دارد که هر عدد گویا دارای بسط دودویی مختوم یا تکراری است و هر بسط مختومی با یک بسط نامختوم تکراری معادل است! از عکس نقیض این گزاره نتیجه میشود که اعداد گنگ بسط دودویی نامختوم و ناتکراری دارند! بنابراین هر عدد دارای یک بسط نامختوم است اما مشکل اینجاست که کامپیوتر دارای فضای محدود است! برای مثال در دقت معمولی تنها ۳۲ بیت فضا برای ذخیرهی یک عدد اعشاری در نظر گرفته میشود که از آن ۱ بیت برای علامت عدد است، ۸ بیت برای ذخیرهی مکمل دوی نما و ۲۳ بیت برای ذخیرهی ارقام بامعنا (جدول رنگی چند پاراگراف بالاتر را ببینید!). این وسط حتی اگر جمع در یک فضای با دقت بزرگتر انجام شود، در نهایت تعداد محدودی از آن ذخیره میشوند. در این بین انباشتگی بیتها در هنگام جمع در آن فضای بزرگتر موجب میشود تا در هنگام برش، احتمالا تنی چند از بیتها صورت متفاوتی نسبت به آنچه با تبدیل مستقیم بدست میآید را داشته باشند، این همان چیزی که شرط در تصویر اول را غلط میکند و آن ۴ آخر را تولید میکند! در نهایت با این توضیحات رفتار پایتون و جاوااسکریپت و هر زبان دیگری به نظر منطقی است.