به سومین پست و ویدیو آموزش TypeScript از کانال یوتیوب میکروفرانت اند خوش آمدید. در ویدیو قبلی که در این پست هم آمده توضیح دادیم که چرا برنامه نویسی مبتنی بر Type میتواند جلوی بسیاری از باگ های مهم در نرم افزار را بگیرد.
در سومین ویدیو از پلی لیست تایپ اسکریپت، نوع داده عدد را در این زبان بررسی کردیم. با این پرسش آغاز کردیم که چرا 0.1+0.1+0.1 برابر 0.3 نمیشود. سپس به بررسی ویژگیهای کلی نوع عدد در زبانهای برنامه نویسی پرداختیم و width و encoding را بررسی کردیم و مفاهیم overflow و underflow راههای کنترل آن را معرفی کردیم.
در نهایت فرمت binary64 از انکدینگ IEEE 754 که برای اعداد در جاوا اسکریپت و بسیاری از زبانهای برنامه نویسی استفاده میشود را توضیح و راههای مقایسه مطمن اعداد اعشاری و صحیح را در جاوا اسکریپت شرح دادیم
اعداد به صورت کلی در زبانهای برنامه نویسی به صورت یک یا چند نوع اصلی مطرح میشوند. نکات مهمی وجود دارد که در هنگام کار با اعداد بایستی مد نظر داشته باشید. به عنوان مثال حاصل 0.1 + 0.1 + 0.1 برابر با 0.3 نخواهد شد و عدم در نظر داشتن موارد اینچنینی منجر به بروز باگ های خطرناک در نرم افزار خواهد شد.
برای درک بهتر این موارد بایستی ابتدا شیوه ذخیره اعداد در کامپیوتر را بررسی نماییم. دو مشخصه مهم Width و Encoding ساختار کلی نوع عدد را تعیین میکند. Width تعداد بیتهایی که برای نمایش عدد لازم است را مشخص میکند که بسیار وابسته به معماری سخت افزاری است و Encoding ساختار این ذخیره سازی را مشخص میکند که معمولا به صورت باینری بدون علامت، مکمل دو یا IEEE754 خواهد بود.
در حالت باینری بدون علامت همه بیتهای در نظر گرفته شده برای عدد رزرو میشد. این روش فقط مناسب اعداد صحیح است و اگر نیاز به ذخیره اعداد علامت دار داشته باشیم بایستی از روش مکمل دو استفاده کنیم در این روش یک بیت برای علامت رزرو میشود و ۱ به معنی منفی و ۰ به معنی مثبت است. اگر عدد مثبت باشد به همان روش باینری ذخیره میشود و اگر منفی باشد مکمل دو آن عدد ذخیره میشود.
چالش زمانی بوجود میآید که نتیجه محاسبات بزرگتر یا کوچکتر از width شود که به آن به ترتیب overflow و underflow میگوییم. برای حل این معضل سه روش معمولا در زبانهای برنامه نویسی به کار گرفته میشود:
استاندارد و انکودینگ IEEE 754 برای ذخیره اعداد به صورت ممیز شناور معرفی شده است و در جاوا اسکریپت و به طبع آن در TypeScript از فرمت binary64 این encoding برای همه اعداد استفاده میشود که شامل سه مولفه اصلی یک بیت علامت، ۱۱ بیت نما و ۵۲ بیت مانتیس است. نکته مهمی که باید در مورد این فرمت بخاطر داشت این است که چون یک الگوریتم فشرده سازی نیز هست، هدف ذخیره تا حد امکان عدد برزگتر در فضای محدود است و سعی میکند قسمت اعشار را round کند بر این اساس ممکن است بخشی از دقت بدلیل خطای rounding از بین برود.
در چنین شرایطی اگر نیاز به دقت بالا در محاسبات داشته باشیم بهتر است که قسمت اعشار و قسمت صحیح را جداگانه بهصورت صحیح ذخیره کنیم.
در مورد اعداد صحیح چنانچه عدد بسیار بزرگ شود نیز ممکن است rounding اتفاق بیافتد که با متد Number.isSafeInteger برای چک کردن این مورد استفاده میشود
بدلیل خطای rounding، به شکل کلی مقایسه برابری اعداد ممیز شناور یا اعشاری ممکن است ایده خوبی نباشد، راه بهتر این است که بگویم دو مقدار تقریبا یکی هستند. برای این کار بایستی حداکثر خطای ممکن در rounding را که مرتبط به encoding است و آن را machine epsilon میگویند بدانیم و آن را به عنوان حد آستانه تقریب در نظر بگیرم. برای دسترسی به این مقدار در JavaScript از Number.EPSILON استفاده میکنیم که کوچکترین عدد بزرگتر از یک تفسیر میشود. با این اوصاف میتوان از کد زیر برای مقایسه اعداد ممیز شناور استفاده کرد.
function epsilonEqual(a: number, b: number): boolean { return Math.abs(a - b) <= Number.EPSILON; } console.log(0.1 + 0.1 + 0.1 == 0.3); //false console.log(epsilonEqual(0.1 + 0.1 + 0.1, 0.3));
این مطلب برداشتی از فصل دوم کتاب Programming with type نوشته Vlad Riscutia است که منبع اصلی این دوره آموزشی است.
برای مشاهده این دوره به صورت کامل می توانید به پلی لیست آموزش برنامه نویسی typescript در کانال یوتیوب میکروفرانتاند مراجعه کنید.