رفتن به محتوای اصلی
توضیح کوهستانمحاسبات ممیز شناورتوضیح معماری· 5 دقیقه مطالعه· در فناوری

چرا در برنامه‌نویسی مدرن، حاصل ۰.۱ + ۰.۲ برابر با ۰.۳ نیست؟

استاندارد IEEE 754 کامپیوترها را مجبور می‌کند تا اعداد حقیقی نامتناهی را در فضای محدود ۶۴ بیتی تخمین بزنند، که این امر منجر به خطاهای گرد کردن بسیار کوچکی می‌شود که در محاسبات مالی و علمی انباشته می‌گردند.

به قلم آیدا امینی

این خبر را به اشتراک بگذارید

به‌طور خلاصه

  • پردازنده‌های مدرن از استاندارد IEEE 754 برای نمایش اعداد حقیقی دقیقاً در ۶۴ بیت حافظه استفاده می‌کنند.
  • از آنجا که کامپیوترها بر مبنای دودویی (باینری) محاسبه می‌کنند، کسرهای اعشاری ساده مانند ۰.۱ به کسرهای تکرارشونده‌ای تبدیل می‌شوند که باید کوتاه شوند.
  • حاصل جمع ۰.۱ و ۰.۲ برابر با ۰.۳۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۴ می‌شود، زیرا سخت‌افزار در حال جمع کامل دو تخمین ناقص است.

کمیته IEEE 754 نحوه درک سیلیکون مدرن از جهان فیزیکی را دیکته می‌کند. هنگامی که معماران سخت‌افزار در شرکت‌هایی مانند اینتل (Intel)، اِی‌اِم‌دی (AMD) یا اپل (Apple) پردازنده جدیدی طراحی می‌کنند، دقیقاً ۶۴ بیت حافظه را برای نمایش هر عدد حقیقی، از جرم یک الکترون گرفته تا فاصله بین کهکشان‌ها، اختصاص می‌دهند. آن‌ها این مصالحه را در استاندارد IEEE 754، که آخرین بار در سال ۲۰۱۹ بازنگری شد، تدوین کرده‌اند.[2]

این محدودیت ۶۴ بیتی یک مرز ریاضی اساسی ایجاد می‌کند. بین ۰.۱ و ۰.۲ بی‌نهایت عدد حقیقی وجود دارد، اما یک رجیستر ۶۴ بیتی تنها می‌تواند دقیقاً ۱۸٬۴۴۶٬۷۴۴٬۰۷۳٬۷۰۹٬۵۵۱٬۶۱۶ حالت منحصر به فرد را نمایش دهد. برای جا دادن بی‌نهایت در سیلیکون محدود، سخت‌افزار مجبور است تقریباً هر عددی را که با آن مواجه می‌شود، تخمین بزند و آن را به نزدیک‌ترین مقدار دودویی موجود تبدیل کند.[1][3]

این مشکل بلافاصله با کسرهای اعشاری ساده خود را نشان می‌دهد. در ریاضیات مبنای ۱۰، کسر ۱/۱۰ به صورت ۰.۱ نوشته می‌شود. اما کامپیوترها بر مبنای ۲ (دودویی) کار می‌کنند. در مبنای دودویی، ۰.۱ به یک کسر تکرارشونده تبدیل می‌شود: ۰.۰۰۰۱۱۰۰۱۱۰۰۱۱ که تا ابد ادامه می‌یابد و نمی‌توان آن را به طور کامل حل کرد.

نحوه تقسیم ۶۴ بیت حافظه برای نمایش یک عدد ممیز شناور.

از آنجا که پردازنده تنها ۵۳ بیت را برای مانتیس (significand) – بخشی از عدد ممیز شناور که ارقام معنی‌دار را در خود نگه می‌دارد – اختصاص داده است، در نهایت باید این دنباله نامتناهی را کوتاه کند. سخت‌افزار کسر دودویی تکرارشونده را در بیت ۵۳ گرد می‌کند و مقدار را به طور دائمی تغییر می‌دهد، حتی پیش از آنکه هرگونه عملیات ریاضی انجام شود.[2][4]

دیوید گلدبرگ (David Goldberg) در مقاله بنیادی خود در سال ۱۹۹۱ در ACM Computing Surveys اشاره می‌کند که «محاسبات ممیز شناور از نظر بسیاری از افراد موضوعی تخصصی و پیچیده تلقی می‌شود»، اما پیامدهای این کوتاه کردن بسیار مشهود است. وقتی یک توسعه‌دهنده از برنامه می‌خواهد ۰.۱ را ذخیره کند، واحد پردازش مرکزی (CPU) در واقع مقدار 0.1000000000000000055511151231257827021181583404541015625 را ذخیره می‌کند.[1]

همین خطای کوانتیزاسیون (quantization error) در مورد ۰.۲ نیز صدق می‌کند، که در حافظه به صورت 0.200000000000000011102230246251565404236316680908203125 ذخیره می‌شود. هنگامی که واحد منطق و محاسبات (ALU) این دو تخمین را با هم جمع می‌کند، عملیات ریاضی را به طور کامل بر روی ورودی‌های ناقص اجرا می‌کند.

حاصل جمع دقیق آن دو مقدار ذخیره‌شده برابر است با 0.3000000000000000444089209850062616169452667236328125. هنگامی که زبان برنامه‌نویسی این نتیجه را برای نمایش روی صفحه کاربر به مبنای ۱۰ برمی‌گرداند، آن را به نزدیک‌ترین عدد اعشاری قابل نمایش گرد می‌کند و نتیجه بدنام 0.30000000000000004 حاصل می‌شود.[6]

این یک باگ در پایتون (Python)، جاوا اسکریپت (JavaScript) یا سی پلاس پلاس (++C) نیست. این رفتار مورد نظر استاندارد IEEE 754 است که در ابتدا در سال ۱۹۸۵ تحت رهبری ویلیام کاهان (William Kahan)، برنده جایزه تورینگ، تدوین شد. کمیته عمداً سرعت محاسباتی و دامنه دینامیکی عظیم را بر دقت کامل مبنای ۱۰ ترجیح داد.[1][4]

با استانداردسازی این فرمت، تولیدکنندگان سخت‌افزار توانستند واحدهای ممیز شناور اختصاصی (FPUs) را مستقیماً در سیلیکون بسازند. این امر به پردازنده‌های مدرن اجازه می‌دهد تا میلیاردها مورد از این تخمین‌ها را در ثانیه اجرا کنند و رندرینگ سه‌بعدی بی‌درنگ، کدگذاری ویدئو و شبیه‌سازی‌های پیچیده فیزیک را بدون کند کردن CPU ممکن سازند.

واحدهای ممیز شناور سخت‌افزاری عملیات را به صورت نمایی سریع‌تر از کتابخانه‌های اعشاری مبتنی بر نرم‌افزار اجرا می‌کنند.

با این حال، این مصالحه بر سر سرعت در نرم‌افزارهای مالی فاجعه‌بار می‌شود. یک برنامه بانکی که از اعداد ممیز شناور برای محاسبه نرخ بهره ۵ درصدی بر روی موجودی ۱۰٬۰۰۰ دلاری استفاده می‌کند، در نهایت این خطاهای گرد کردن میکروسکوپی را به کسری از سنت‌های مفقود انباشته می‌کند و دفاتر حسابداری سخت‌گیرانه را نقض می‌نماید.[5]

اِوان جونز (Evan Jones)، مهندس نرم‌افزار، استدلال می‌کند: «شما می‌توانید از اعداد ممیز شناور برای پول استفاده کنید»، به شرطی که توسعه‌دهنده مقادیر را به شدت به اعداد صحیح زیر ۲ به توان ۵۳ (دقیقاً ۹٬۰۰۷٬۱۹۹٬۲۵۴٬۷۴۰٬۹۹۲) محدود کند تا به طور کامل از کوانتیزاسیون کسری جلوگیری شود. اما اکثر مؤسسات مالی به جای آن، استفاده از کتابخانه‌های اعشاری با دقت دلخواه را الزامی می‌دانند و سرعت خام پردازنده را با قطعیت ریاضی معاوضه می‌کنند.[5]

در محاسبات علمی، شرایط متفاوت است. محققانی که مدل‌های دینامیک سیالات یا آب و هوا را اجرا می‌کنند، به دلیل دامنه دینامیکی عظیم محاسبات ممیز شناور به آن متکی هستند. یک ممیز شناور ۶۴ بیتی می‌تواند مقادیری به کوچکی ۲.۲۲ × ۱۰ به توان منفی ۳۰۸ و به بزرگی ۱.۷۹ × ۱۰ به توان ۳۰۸ را نمایش دهد، که به یک شبیه‌سازی واحد اجازه می‌دهد هم ذرات میکروسکوپی و هم جرم‌های سیاره‌ای را ردیابی کند.

خطر در این شبیه‌سازی‌ها «حذف فاجعه‌بار» (catastrophic cancellation) است. اگر یک مدل فیزیکی دو عدد ممیز شناور تقریباً مساوی را از هم کم کند، ارقام معنی‌دار حذف می‌شوند و تنها خطای کوانتیزاسیون باقی می‌ماند. این خطا سپس در محاسبات بعدی منتشر می‌شود و به طور بالقوه کل شبیه‌سازی را بی‌اعتبار می‌سازد.[1]

برای کاهش این مشکل، دانشمندان داده به مفهوم اپسیلون ماشین (machine epsilon) – کران بالای خطای نسبی تخمین – متکی هستند. برای ممیزهای شناور با دقت مضاعف (double-precision)، اپسیلون ماشین ۲ به توان منفی ۵۲ یا تقریباً ۲.۲۲ × ۱۰ به توان منفی ۱۶ است. الگوریتم‌ها باید به صراحت طوری طراحی شوند که خطاهای تجمعی را به خوبی بالاتر از این آستانه نگه دارند.[3][6]

از آنجا که ۰.۱ نمی‌تواند به طور کامل در مبنای ۲ نمایش داده شود، سخت‌افزار کسر تکرارشونده را کوتاه می‌کند.

در حالی که صنعت محاسبات به سمت هوش مصنوعی حرکت می‌کند، استاندارد IEEE 754 دوباره در حال انطباق است. مدل‌های یادگیری ماشین به ۵۳ بیت دقت نیاز ندارند؛ آن‌ها به توان عملیاتی عظیم نیاز دارند. این امر باعث شده است تا فرمت‌های ممیز شناور ۱۶ بیتی و ۸ بیتی کوچک‌تر پذیرفته شوند، که دقت را با توانایی جای دادن پارامترهای بیشتر در حافظه پردازنده گرافیکی (GPU) معاوضه می‌کنند.[2]

عجیب بودن ۰.۱ به علاوه ۰.۲ یک ویژگی دائمی در علوم کامپیوتر باقی مانده است. این موضوع به عنوان درسی اجباری برای هر برنامه‌نویس تازه‌کار عمل می‌کند: ماشین ریاضیات انسانی را درک نمی‌کند، بلکه تنها مرزهای محدود سیلیکونی را می‌فهمد که برای احترام به آن‌ها مهندسی شده است.[7]

اصطلاحات کلیدی

ممیز شناور (Floating-point)
روشی برای نمایش اعداد حقیقی در محاسبات که با معاوضه دقت با دامنه دینامیکی، طیف وسیعی از مقادیر را پشتیبانی می‌کند.
مانتیس (Significand)
بخشی از یک عدد ممیز شناور که ارقام معنی‌دار آن را در بر می‌گیرد و دقت مقدار را تعیین می‌کند.
اپسیلون ماشین (Machine epsilon)
کران بالای خطای نسبی ناشی از گرد کردن در محاسبات ممیز شناور؛ کوچک‌ترین تفاوتی که ماشین می‌تواند تشخیص دهد.
حذف فاجعه‌بار (Catastrophic cancellation)
از دست دادن شدید دقت که زمانی رخ می‌دهد که دو عدد ممیز شناور تقریباً مساوی از هم کم می‌شوند و تنها خطاهای گرد کردن آن‌ها باقی می‌ماند.

پرسش‌های متداول

چرا ۰.۱ + ۰.۲ برابر با ۰.۳۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۴ می‌شود؟

زیرا کامپیوترها در مبنای ۲ (دودویی) کار می‌کنند، جایی که ۰.۱ و ۰.۲ کسرهای تکرارشونده هستند. سخت‌افزار این کسرها را کوتاه می‌کند تا در ۶۴ بیت حافظه جا شوند، و جمع کردن این دو تخمین کوتاه شده، عددی کمی بزرگتر از ۰.۳ به دست می‌دهد.

آیا این یک باگ در زبان برنامه‌نویسی من است؟

خیر. تقریباً تمام زبان‌های مدرن، از جمله پایتون، جاوا اسکریپت و ++C، از استاندارد IEEE 754 برای محاسبات ممیز شناور استفاده می‌کنند. زبان برنامه‌نویسی به درستی نتیجه دقیق تخمین سخت‌افزار را بازمی‌گرداند.

چگونه می‌توانم پول را به طور دقیق در کد محاسبه کنم؟

هرگز از اعداد ممیز شناور برای ارز استفاده نکنید. در عوض، از کتابخانه اعشاری داخلی زبان خود (مانند ماژول 'decimal' در پایتون) استفاده کنید یا پول را به صورت اعداد صحیح که کوچک‌ترین واحد (مانند سنت) را نشان می‌دهند، ذخیره کنید.

بررسی عمیق دیدگاه‌ها

معماران سخت‌افزار

سرعت محاسباتی و کارایی سیلیکون را بر دقت مبنای ۱۰ اولویت می‌دهند.

برای مهندسانی که سیلیکون فیزیکی CPU را طراحی می‌کنند، ریاضیات ممیز شناور یک پیروزی در کارایی است. با استانداردسازی فرمت ۶۴ بیتی، آن‌ها می‌توانند واحدهای ممیز شناور اختصاصی (FPU) بسازند که عملیات ریاضی را در یک چرخه ساعت اجرا می‌کنند. آن‌ها استدلال می‌کنند که اگر سخت‌افزار مجبور بود برای ارضای شهود انسانی در مبنای ۱۰ محاسبه کند، پردازنده‌ها به صورت نمایی کندتر می‌شدند و انرژی بسیار بیشتری مصرف می‌کردند، که این امر گرافیک و بازی‌های مدرن را فلج می‌کرد.

توسعه‌دهندگان نرم‌افزار مالی

محاسبات ممیز شناور را به طور کامل برای ارز رد می‌کنند و به کتابخانه‌های اعشاری با دقت دلخواه متکی هستند.

در بخش مالی، خطای گرد کردن 0.00000000000000004 یک شکست حیاتی محسوب می‌شود. توسعه‌دهندگان مالی استدلال می‌کنند که ریاضیات ممیز شناور اساساً با دفاتر حسابداری ناسازگار است، جایی که هر سنت باید به طور کامل ردیابی شود. آن‌ها به کتابخانه‌های اعشاری مبتنی بر نرم‌افزار متکی هستند که دقیقاً مانند محاسبات انسانی روی کاغذ عمل می‌کنند و آگاهانه سرعت پردازنده را فدای تضمین این می‌کنند که ۰.۱ به علاوه ۰.۲ همیشه دقیقاً برابر با ۰.۳ باشد.

محققان محاسبات علمی

به دامنه دینامیکی عظیم اعداد ممیز شناور متکی هستند اما مرزهای خطا را به طور فعال مدیریت می‌کنند.

دانشمندانی که تغییرات آب و هوا یا مکانیک مداری را مدل‌سازی می‌کنند، نمی‌توانند از اعداد صحیح استفاده کنند؛ آن‌ها نیاز دارند که هم ذرات میکروسکوپی و هم کیهان را در یک معادله نمایش دهند. آن‌ها خطاهای کوانتیزاسیون را به عنوان یک مصالحه ضروری برای این دامنه دینامیکی عظیم می‌پذیرند. تمرکز آن‌ها بر پایداری الگوریتمی است – طراحی مدل‌های ریاضی که از انباشته شدن این تخمین‌های کوچک سخت‌افزاری به حذف فاجعه‌بار در طول میلیون‌ها محاسبه متوالی جلوگیری کند.

معماران سخت‌افزار 40%توسعه‌دهندگان نرم‌افزار مالی 30%محققان محاسبات علمی 30%
معماران سخت‌افزار
سرعت محاسباتی و کارایی سیلیکون را بر دقت مبنای ۱۰ اولویت می‌دهند و استدلال می‌کنند که تخمین‌ها برای عملکرد ضروری هستند.
توسعه‌دهندگان نرم‌افزار مالی
محاسبات ممیز شناور را به طور کامل برای ارز رد می‌کنند و برای جلوگیری از خطاهای انباشته در دفاتر حسابداری، به کتابخانه‌های اعشاری با دقت دلخواه متکی هستند.
محققان محاسبات علمی
به دامنه دینامیکی عظیم اعداد ممیز شناور متکی هستند اما برای جلوگیری از حذف فاجعه‌بار در شبیه‌سازی‌ها، به طور فعال مرزهای خطا را مدیریت می‌کنند.

دیدگاه‌هایی که این گزارش پوشش نداده

  • طراحان کامپایلر
  • مهندسان یادگیری ماشین

منابع

پوشش منابع

7 منبع

3 دیدگاه شناسایی‌شده

معماران سخت‌افزار 40%توسعه‌دهندگان نرم‌افزار مالی 30%محققان محاسبات علمی 30%
  1. [1]Oracleمحققان محاسبات علمی

    What Every Computer Scientist Should Know About Floating-Point Arithmetic

    مطالعه در Oracle →
  2. [2]IEEE SAمعماران سخت‌افزار

    IEEE 754-2019 - IEEE Standard for Floating-Point Arithmetic

    مطالعه در IEEE SA →
  3. [3]Wolfram MathWorld

    Floating-Point Representation

    مطالعه در Wolfram MathWorld →
  4. [4]GeeksforGeeks

    IEEE Standard 754 Floating Point Numbers

    مطالعه در GeeksforGeeks →
  5. [5]evanjones.caتوسعه‌دهندگان نرم‌افزار مالی

    You can use floating-point numbers for money!

    مطالعه در evanjones.ca →
  6. [6]University of Marylandمعماران سخت‌افزار

    The IEEE 754 Format

    مطالعه در University of Maryland →
  7. [7]تیم سردبیری کوهستان

    تحلیل تیم سردبیری کوهستان

    مطالعه در تیم سردبیری کوهستان →

نظرات

همیشه در جریان باشید

هر زاویه. هر روز.

اخبار فناوری با پوشش کامل منابع و تحلیل دیدگاه‌ها، هر روز و رایگان.