أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
البرمجة بالعربي

Floating Point للمبتدئ: ليه 0.1 + 0.2 مش بيساوي 0.3 في كل لغات البرمجة

📅 ٨ مايو ٢٠٢٦⏱ 5 دقائق قراءة
Floating Point للمبتدئ: ليه 0.1 + 0.2 مش بيساوي 0.3 في كل لغات البرمجة

المستوى: مبتدئ

لو كتبت 0.1 + 0.2 في الكود وطلعلك 0.30000000000000004، يبقى المقال ده ليك. هتفهم ليه ده بيحصل في كل لغات البرمجة (مش JavaScript بس)، وامتى لازم تتدخّل، وامتى عادي تتجاهل.

آلة حاسبة تعرض أرقام عشرية تشرح مشكلة دقة الـ floating point في البرمجة

ليه 0.1 + 0.2 مش بيساوي 0.3 في الكمبيوتر

المشكلة باختصار

افتح Console في المتصفح واكتب الأمر ده:

JavaScript
console.log(0.1 + 0.2);
// النتيجة: 0.30000000000000004

اعمل نفس الحاجة في Python:

Python
print(0.1 + 0.2)
# النتيجة: 0.30000000000000004

في Java و C++ و Go و Ruby و Swift و PHP و C#، النتيجة هي هي. ده مش bug في اللغة ولا في المعالج بتاعك. ده سلوك معتمد ومتفق عليه في كل المعالجات الحديثة من سنة 1985.

المثال البسيط — قسمة الكيكة على 3

تخيل إن عندك كيكة عايز تقسمها بالتساوي على 3 ناس. الجواب الحقيقي 0.333333... وعلامة "..." معناها أرقام بتفضل تتكرر للأبد. لكن لو معاك ورقة فيها 8 خانات بس بعد العلامة العشرية، هتكتب 0.33333333 وتقف. الرقم اللي كتبته أقل من الثلث الحقيقي بمقدار صغير جدًا. لو ضربت في 3 الإجابة هتطلع 0.99999999 مش 1 صحيحة.

الكمبيوتر بيعمل نفس الحاجة بالظبط، لكن بالنظام الثنائي (binary) مش العشري. الفرق المهم إن الكمبيوتر مش عنده مشكلة مع 0.5 أو 0.25 أو 0.75 لأنها كسور من قوى 2. المشكلة بتظهر مع 0.1 و 0.2 و 0.3، لأنها كسور دورية لا نهائية في binary.

التعريف العلمي — معيار IEEE 754

الكمبيوتر بيخزّن الأرقام العشرية بصيغة اسمها IEEE 754 double precision في 64 bit مقسومين كالآتي:

  • 1 bit للإشارة (موجب أو سالب)
  • 11 bit للأس (exponent)
  • 52 bit للجزء الكسري (mantissa)

الرقم 0.1 في binary هو 0.0001100110011001100... وهكذا للأبد. التمثيل بـ 52 bit بيقطع التتابع، فيتخزّن كرقم أقرب وهو فعلياً 0.1000000000000000055511151231257827021181583404541015625. ده اللي بيخلّي 0.1 + 0.2 = 0.30000000000000004 بدل 0.3 المفترض.

شاشة تعرض كود برمجي بأرقام binary توضّح تمثيل الأرقام العشرية في معيار IEEE 754

الإصابة الحقيقية — فاتورة بـ 1 سنت غلط

فيه شركة fintech عربية كان عندها مشكلة في حساب فاتورة شهرية بسيطة، الكود كان شبه ده:

JavaScript
let total = 0;
for (let i = 0; i < 30; i++) {
  total += 0.1; // كل يوم بـ 0.1 جنيه
}
console.log(total);            // 2.9999999999999996
console.log(total === 3.0);    // false

التحقق total === 3.0 بيرجع false. المنطق التجاري كان مبني على المساواة دي عشان يقفل الفاتورة. النتيجة: العميل كان بيدفع 0.01 جنيه زيادة في 12,000 فاتورة شهرية. الخسارة الإجمالية: 120 جنيه شهرياً بدون أي سبب واضح في الـ logs، والفريق فضل شهرين يدوّر على السبب قبل ما يفهموا إن المشكلة في الـ float.

الحل — اختار الأداة الصح للموقف

مفيش حل واحد بيناسب كل الحالات. ده اللي بنستخدمه فعلاً في الإنتاج:

  1. للحسابات المالية: استخدم decimal type. في Python from decimal import Decimal، في Java BigDecimal، في JavaScript مكتبات زي decimal.js أو big.js.
  2. للمقارنات بين أرقام عشرية: ما تستخدمش === أصلاً. استخدم تفاوت مقبول: Math.abs(a - b) < 0.0001. الرقم 0.0001 بيتسمى epsilon والقيمة بتعتمد على الـ domain.
  3. للعملات تحديداً: اخزن الفلوس بالـ cents كأرقام صحيحة. 1 جنيه = 100 قرش = integer 100. الجمع والطرح والضرب كلهم على integers، ومافيش أخطاء على الإطلاق. ده اللي PayPal و Stripe بيعملوه.

مثال Python بـ Decimal:

Python
from decimal import Decimal

result = Decimal('0.1') + Decimal('0.2')
print(result)                     # 0.3 بالظبط
print(result == Decimal('0.3'))   # True

لاحظ إن Decimal بياخد الأرقام كـ string مش كـ float، عشان لو كتبت Decimal(0.1) هتاخد الـ float المغشوش أصلاً، فاللي هيتخزن هو 0.1000000000000000055511...

Trade-offs — مفيش حل ببلاش

  • Decimal أبطأ من float بـ 20x تقريباً. على Python 3.12، 1 مليون عملية جمع بالـ float بتاخد 35ms، نفسها بـ Decimal بتاخد حوالي 720ms.
  • integer cents بيخلّي القسمة معقدة. 100 قرش ÷ 3 = 33.33 قرش، فيه فاقد قرش لازم تتعامل معاه يدوياً (rounding rules رسمية في كل دولة).
  • Decimal بياخد ذاكرة أكتر. float = 8 bytes، أما Decimal('0.1') = حوالي 104 bytes في Python.
  • المقارنة بـ epsilon بتفقد دقّة الـ test cases. الـ unit test ممكن يقبل قيم غلط بمقدار صغير لكنه فعلاً مهم في الـ domain بتاعك.

متى لا تتدخّل أصلاً

لو شغلك في graphics أو physics simulation أو machine learning، الـ float العادي كافي تماماً. الفرق بمقدار 0.0000000004 مش هيتلاحظ بصرياً ولا هيأثر في النتيجة النهائية. الحوادث المعروفة كلها في domains فيها equality قاطعة ومحاسبة دقيقة: حسابات مالية، أنظمة تذاكر الطيران، vote tallying في الانتخابات، قياسات هندسية بدقة عالية. لو domain شغلك مش واحد من دول، اتركه float واشتغل.

الخطوة التالية

افتح أكبر codebase عندك واعمل grep على 0.1 و === مع أرقام عشرية. كل match فيهم احتمال واقعي يكون فيه bug صامت. لو لقيت match في كود مالي، حوّله integer cents فوراً قبل ما تكتشف الفاتورة الغلط في حساب عميل مهم.

المصادر

  • IEEE Standard 754-2019 for Floating-Point Arithmetic — المعيار الرسمي
  • Python Tutorial: Floating Point Arithmetic — Issues and Limitations
  • 0.30000000000000004.com — قائمة بأكتر من 60 لغة برمجة كلهم بيدّوا نفس النتيجة
  • David Goldberg, "What Every Computer Scientist Should Know About Floating-Point Arithmetic", ACM Computing Surveys, March 1991
  • Python decimal module documentation

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة