این نوشته ، چکیده و ترجمه مقاله ای با همین نام در مدیوم هست .
حتما براتون سوال شده که چرا تو زبان هایی مثل سی شارپ موقع نوشتن اعداد اعشاری float، آخر متغیر یه f اضافه میکنیم .
تو این مقاله درمورد دلیل استفاده از انواع متغیر ها صحبت میکنیم و بررسی اینکه در سخت افزار دقیقا چه اتفاقاتی میافته وقتی داریم با این اعداد کار میکنیم .
تو زبان های سطح بالایی مثل پایتون و جاوا اسکریپت اعداد اعشاری خیلی راحت نوشته میشن، یعنی عدد به علاوه علامت اعشار. اما تو زبان هایی مثل C یا #C قضیه یه کم متفاوته. تو زبان C خیلی راحت میتونیم با عوض کردن کلمه کلیدی جلوی متغیر موقع declaration، به انواع تایپ ها بریم مثل float ,double, short و long ولی در زبان سی شارپ فقط به یه f نیاز داریم .
اما دلیلش چیه ؟
راستش کامپیوتر ها خیلی هم دقیق نیستن، ممکنه فکر کنید محاسبات رو با دقت بالا انجام میدن و هیچوقت دچار خطا نمیشن و اگه مشکلی هم هست تقصیر برنامه نویس هست که به درستی از ابزارهای دیباگینگ و ... استفاده نکرده . اما نوع هایی از ارور هستن که هیچ ربطی به استفاده اشتباه از نوع متغیر یا استفاده از یه الگوریتم بد ندارن. بلکه مربوط به محدودیت های سخت افزار میشه و روشی که کامیپوتر اعداد رو ارائه میکنه .
به این نوع از ارور ها میگیم Floating point arithmetics error
که مبحث خیلی گسترده ای هست و زیر مجموعه علوم کامپیوتر و ریاضیات محسوب میشه و در تلاش برای پیدا کردن راهی هستیم که بتونیم هم اعداد خیلی کوچیک و هم خیلی بزرگ رو به کار ببریم بدون اینکه دچار ارور بشیم.
اما چرا ؟ این برمیگرده به روش ذخیره سازی اعداد توی کامپیوتر . قطعا از سال ۱۹۶۰ تا الان برای ذخیره سازی اعداد تو کامپیوتر روشی پیدا شده ولی بحثی که ما اینجا داریم برای اعداد معمولی نیست بلکه اعداد خیلی بزرگ یا خیلی کوچیک هستن که مشکل ساز میشن.
سال ۱۹۹۱ وقتی آمریکا میخواست موشک بفرسته تا بتونه موشک اسکاد عراقی رو ردگیری کنه ، موشک آمریکایی از مسیرش منحرف میشه و مکان اشتباهی پرتاب میشه. فقط به خاطر یه خطای گرد کردن .
تو جایی از برنامه باید زمان رو در دهم ثانیه بگیریم و در 0.1 ضرب کنیم تا مقدار عدد رو ثانیه بدست بیاریم ولی سیستم قادر نبود این تعداد از اعشار رو مدیریت کنه و بعد انجام عملیات نتیجه رو در حد خیلی کمی تغییر میداد وقتی میگم خیلی کم منظورم اینه در حد : 0.000000095
و بعد از تکرار این عملیات ها و سرعت بالای اون باعث شد که موشک نیم کیلومتر از هدف خودش منحرف بشه.
از یه جایی به بعد کامپیوتر فرقی بین اعداد قرار نمیده و اونارو یکسان میبینه. مثلا یه کامپیوتر خیلی کوچیک که توانایی ذخیره بیشتر از هشت رقم اعشار نداره این دوتا عدد رو یکسان میدونه :
0.000012341
0.000012344
خب ممکنه بگین که میتونیم تا حد ممکن اعشار اضافه کنیم و امیدوار باشیم که نتیجه بده اما هیچ چیز رایگان نیست و هزینه ای داره ! افزایش دقت، نیاز به حافظه بیشتر برای اجرا داره و محاسبات رو کاهش میده اما نکته اینجاست که باید یه تعادل بین دقت ، مصرف حافظه و زمان اجرا پیدا کنید .
در ادامه شرایط مخلتفی که به این دقت نیاز نداریم رو مثال میزنیم ...
بازی های ویدیویی
به ندرت یا هرگز به مقدار دقیق نیاز پیدا میکنیم. به همین دلیل هست که از float موقع برنامه نویسی با Unity سی شارپ استفاده میکنیم ، فقط باید مطمئن بشیم که کد با حداکثر سرعت ممکن برنامه اجرا بشه. مثلا جایگزینی اشتباه آواتار شخصیت در حد یک صدم هزارم واحد، برای چشم انسان غیرقابل توجه هست.
درمورد این همه انواع داده ای که داریم باید بگم در واقع روش هایی هستن که به کامپیوتر بگیم از حافظه کم یا زیاد برای ذخیره متغیر استفاده کنه و بتونیم خطای گرد کردن رو کنترل کنیم همچنین نحوه نمایش رو بهینه کنیم با این روش که به کامپیوتر بگیم چه محدود عددی رو انتظار داریم .
مثلا int که خیلی ساده تر از float هست رو داریم که در حالت معمول (البته بستگی به سیستم عامل داره) ولی شونزده بیت حافظه اشغال میکنه : یعنی 16^2.
و اگه بخوایم بیشتر از این محدوده استفاده کنیم دچار overflow میشیم که دوتا گزینه داریم :
یا تعداد بیت ها رو زیاد میکنیم مثلا ۳۲ بیت تا محدوده بزرگتر بشه . مثل نوع long
و روش بعدی روی محدوده خاصی تمرکز میکنیم با استفاده از نوع unsigned ، مثل uint که اینم اعداد صحیح هست ولی فقط اعداد صحیح مثبت رو شامل میشه ولی این بار تو دامنه بزرگتر .
اما problematic types ها اینجا float هستن علاوه بر قسمت صحیح یه قسمت اعشار و قسمت علامت اعشار هم دارن که باعث ایجاد خطای گرد کردن میشه . برای کار با اعداد بزرگ میتونیم از double استفاده کنیم اما مسئله به این سادگی نیست و در حال حاضر استانداردی هست که با پیروی کردن از اون ها تا حدی این خطا ها کنترل میشه : مشکلات گرد کردن و سر ریز و تقسیم بر صفر .
نتیجه :
در پایان فقط باید یه f اضافه کنیم که کامپایلر سر ما فریاد نزنه ولی وقتی که به جای float ، double استفاده میکنید اتفاقات زیادی داخل سخت افزار شما میافته. پس یادتون باشه، کجا و از چه نوع داده ای دارین استفاده میکنید!