مستوى المقال: مبتدئ. هنبدأ بمثال بسيط جدًا، وبعدين نرجع نشرح السبب العلمي بالتفصيل والكود. مش محتاج أي رياضيات متقدمة، بس تعرف تجمع كسور.
ليه 0.1 + 0.2 مش بيساوي 0.3؟
افتح أي Python أو Node.js دلوقتي واكتب 0.1 + 0.2. النتيجة اللي هتطلعلك مش 0.3، دي 0.30000000000000004. بعد المقال ده هتعرف بالظبط ليه ده بيحصل، وإزاي تمنعه من إنه يضيّع فلوس عملائك أو يكسر شرط if في كودك.
المشكلة باختصار
ركز في السطر ده: تقريبًا كل لغة برمجة بتديك نفس النتيجة الغريبة. جرّبها في JavaScript وJava وC وGo وPHP، كلهم بيقولوا إن 0.1 + 0.2 مش بيساوي 0.3. الناس بتفتكر إن ده باج في اللغة. مش باج. ده سلوك مقصود ومتفق عليه في معيار عالمي اسمه IEEE 754. المشكلة مش في اللغة، المشكلة في إن الكمبيوتر بيخزّن الأرقام بنظام مختلف عن اللي احنا متعوّدين عليه.
الأول بمثال بسيط: ثُلث الكوب
تعالَ نسيب الكمبيوتر شوية. إنت بتكتب الأرقام بالنظام العشري، أساسه 10. جرّب تكتب ثُلث (1/3) بالعشري. هتكتب 0.3333 وتفضل تكتب تلاتات لحد ما تزهق، بس الرقم مبيخلصش أبدًا. لو قرّبته لـ 0.33 وجمعته 3 مرات، هيطلعلك 0.99 مش 1. ضاع منك جزء صغير في التقريب.
الكمبيوتر بيشتغل بالنظام الثنائي، أساسه 2، يعني 0 و1 بس. في النظام ده، الرقم 0.1 بالظبط زي الثُلث عندنا: كسر مالوش نهاية. بيتكتب 0.0001100110011... ويفضل يكرّر لحد ما يخلص المكان المتاح. فالكمبيوتر بيقرّبه ويخزّن أقرب رقم لـ 0.1، مش 0.1 بالظبط. ولمّا تجمع رقمين كل واحد فيهم مقرّب، الأخطاء الصغيرة بتتجمّع وتظهرلك في آخر الرقم.
الشرح العلمي بالتفصيل
دلوقتي بالدقة. النوع اللي بتتخزّن فيه الأرقام العشرية في أغلب اللغات اسمه double يعني دقة مزدوجة بـ 64 بت، حسب معيار IEEE 754. الـ 64 بت دول متقسمين: بت واحد للإشارة، و11 بت للأس (exponent)، و52 بت للجزء الكسري (mantissa). الـ 52 بت دول بيدّوك دقة حوالي 15 إلى 17 رقم عشري معنوي بس، مش أكتر.
عشان كده 0.1 مبيتخزّنش 0.1. بيتخزّن فعليًا الرقم ده: 0.1000000000000000055511151231257827.... الفرق صغير جدًا، حوالي 5.5 في 10 أس -18، بس هو موجود. وبالمثل 0.2 بيتخزّن بفرق بسيط. لمّا تجمعهم، مجموع الفرقين بيطلع كبير كفاية عشان يظهر في الخانة الأخيرة، فتشوف 0.30000000000000004.
السيناريو الواقعي: الفلوس بتضيع
الموضوع ده مش نظري. لو عندك متجر إلكتروني بيبيع منتج بـ 0.10 دولار، وعميل ضاف 1000 قطعة في السلة، وحسبت الإجمالي بـ float بالجمع المتكرر، ممكن يطلعلك 100.00000000000007 بدل 100.00. القيمة دي لوحدها مش كارثة، بس أول ما تقارنها بـ == 100.00 عشان تفعّل خصم أو تطابق فاتورة، الشرط هيفشل من غير سبب واضح. وفي أنظمة محاسبية بتعالج ملايين العمليات في اليوم، الفروق دي بتتراكم وتعمل اختلافات في التقارير المالية بتاخد ساعات عشان تصطاد سببها.