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

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

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

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

المنصة

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

الدعم

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

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

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

Floating Point بالعربي: ليه 0.1 + 0.2 ≠ 0.3 وإزاي بتخسر فلوس

📅 ١٩ أبريل ٢٠٢٦⏱ 5 دقائق قراءة
Floating Point بالعربي: ليه 0.1 + 0.2 ≠ 0.3 وإزاي بتخسر فلوس

لو كتبت في JavaScript أو Python الأمر 0.1 + 0.2 وطبعت النتيجة، هتلاقي 0.30000000000000004 مش 0.3. ده مش bug في اللغة، وده مش خطأ في الـ CPU. ده سلوك رسمي في معيار IEEE 754 اللي بتشتغل بيه كل اللغات تقريبًا. المقال ده هيفهّمك ليه ده بيحصل بمثال بسيط، ثم بالتفسير العلمي، وهيوريك سيناريو بيخسّر فلوس فعلًا في production، وطرق العلاج بكود شغّال.

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

الكمبيوتر بيخزّن الأرقام العشرية بصيغة ثنائية (binary)، مش عشرية. فيه أرقام عشرية بسيطة زي 0.1 مش ممكن تتمثّل بدقة كاملة في binary، زي ما 1÷3 مش ممكن تتكتب في decimal غير كـ 0.333... بلا نهاية. الكمبيوتر بيقرّب الرقم عند حد معيّن، والخطأ الصغير ده بيتراكم ويطلع في النتيجة.

أرقام ثنائية زرقاء متتالية على شاشة كمبيوتر تمثّل تخزين الأعداد العشرية بصيغة IEEE 754

الشرح المبسّط أولًا: قصة الثلث

تخيّل إنك ماسك آلة حاسبة وقلت لها احسب 1 ÷ 3. الناتج هيظهر 0.3333333، والآلة هتوقف عند عدد محدد من الخانات. لو جمعت النتيجة على نفسها 3 مرات، المفروض ترجع لـ 1، بس هتلاقيها 0.9999999. السبب: الآلة قطعت الرقم وضيّعت جزء منه.

ده بالظبط اللي بيحصل للكمبيوتر، لكن في نظام العدّ الثنائي. الرقم 0.1 بسيط جدًا في decimal، لكنه في binary بيطلع رقم متكرّر لا نهائي: 0.0001100110011001100... وبما إن الذاكرة محدودة (64 bit في double precision)، الكمبيوتر بيقطع الرقم عند حد معيّن، والباقي بيضيع. لمّا تجمع 0.1 + 0.2، كل واحد فيهم فيه خطأ تقريب، والخطأين بيتجمعوا، فيطلع الناتج 0.30000000000000004.

الفكرة ببساطة: عين الكمبيوتر بتشوف 0.1 كإنه 0.1000000000000000055511151231257827...، ولما بتضيف عليه 0.2 (اللي هو كمان مش مضبوط)، الخطأ بيبان في أول 17 خانة.

التفسير العلمي الدقيق: IEEE 754

المعيار اللي بيحكم الأرقام العشرية في JavaScript وPython وC وJava وGo وأغلب اللغات هو IEEE 754. الصيغة الأشهر هي double precision = 64 bit، متقسّمين كالتالي:

  • 1 bit للإشارة (موجب أو سالب)
  • 11 bit للأس (exponent)
  • 52 bit للـ mantissa (الخانات العشرية الفعلية)

لإن الـ mantissa محدودة بـ 52 bit، أي رقم binary دوري بيتقطع عند الحد ده. أقل فرق ممكن يتمثّل قرب الرقم 1 اسمه Number.EPSILON في JavaScript وقيمته تقريبًا 2.220446049250313e-16. أي خطأ أصغر من ده الكمبيوتر مش بيعرف يميّزه.

سيناريو واقعي: خسارة 14 ألف جنيه في شهر

تخيّل موقع e-commerce بيبيع 50,000 طلب في الشهر، ومتوسط قيمة الطلب 300 جنيه، والضريبة 14%. لو حسبت الضريبة بـ price * 0.14 في JavaScript لكل طلب وخزّنت الناتج كـ float، كل طلب ممكن يكون غلط بمقدار جزء من الألف جنيه. على 50,000 طلب، الفرق بين اللي قيّدته والرقم اللي الـ auditor بيحسبه يدويًا ممكن يوصل 10-15 ألف جنيه في الشهر، وده بيفتحلك مشكلة محاسبية مع مصلحة الضرائب مش بس bug تقني.

آلة حاسبة بجوار فواتير توضّح حالة حساب فلوس وخطأ تقريب عشري في النتيجة

المشكلة بكود JavaScript وPython

JavaScript
// JavaScript
console.log(0.1 + 0.2);            // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);    // false

// خطأ تقييم سعر + ضريبة
const price = 19.99;
const taxRate = 0.14;
const total = price + price * taxRate;
console.log(total);                 // 22.7886 … فعليًا 22.788599999999998
Python
# Python
print(0.1 + 0.2)                    # 0.30000000000000004
print(0.1 + 0.2 == 0.3)             # False

# نفس المشكلة في حساب الفاتورة
price = 19.99
tax = price * 0.14
print(price + tax)                  # 22.788599999999998

4 طرق علاج موثّقة

  1. استخدم أعداد صحيحة (integer cents): بدل ما تخزّن 19.99 جنيه، خزّن 1999 قرش. كل العمليات الحسابية على integer مضبوطة 100%. هي الطريقة اللي Stripe بتستخدمها فعلًا.
  2. نوع Decimal المدمج: في Python استخدم decimal.Decimal، في Java BigDecimal، في C# decimal. أبطأ من float 5–20 ضعف لكن النتيجة مضبوطة.
  3. مقارنة بـ epsilon: لو محتاج تقارن رقمين عشريين وعارف مصدرهم float، استخدم Math.abs(a - b) < Number.EPSILON بدل a === b.
  4. التقريب عند الطباعة فقط: خلّي الحسابات بـ float، وقرّب النتيجة لـ 2 خانة عشرية وقت العرض فقط بـ toFixed(2) في JS أو round(x, 2) في Python.
JavaScript
// الحل الصحيح بـ integer cents
const priceCents = 1999;            // 19.99 جنيه
const taxCents = Math.round(priceCents * 14) / 100;
const totalCents = priceCents + taxCents;
console.log(totalCents / 100);      // 22.79 بالظبط
Python
# الحل الصحيح بـ Decimal
from decimal import Decimal, ROUND_HALF_UP

price = Decimal("19.99")
tax = (price * Decimal("0.14")).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
total = price + tax
print(total)                        # 22.79

الـ trade-offs اللي لازم تعرفها

استخدام Decimal بيكلّفك في الأداء. على مليون عملية ضرب: float في Python بياخد تقريبًا 80ms، Decimal بياخد تقريبًا 1200ms، يعني 15 ضعف أبطأ. في حسابات علمية بكميات ضخمة (ML, image processing, physics simulation) ده فرق كبير. بس في حسابات فواتير وطلبات، الفارق ده مش محسوس لأن الـ throughput بيكون أساسًا محدود بـ I/O مش بالـ CPU.

integer cents أسرع من Decimal بكتير لكنها بتجبرك تفكر في الوحدة (قرش ولا جنيه) في كل مكان، وممكن تغلط لو نسيت تقسم على 100 في مرحلة العرض.

متى لا تستخدم float خالص

  • أي حسابات فلوس: فواتير، ضرائب، رواتب، عمولات. استخدم integer cents أو Decimal.
  • مقارنة مساواة (equality): if (x == 0.3) لازم تتحوّل لـ if (Math.abs(x - 0.3) < 1e-9).
  • Loops بتزود counter عشري: for (let i = 0; i != 1; i += 0.1) ممكن يدخل في infinite loop لإن i مش هتساوي 1 بالظبط.
  • Hashing أرقام عشرية: لأن نفس الرقم ممكن يتحسب بطريقتين ويطلع hash مختلف.

متى float مناسب تمامًا

في حسابات علمية (محاكاة فيزيائية، إحصاء، graphics، ML training) الخطأ 1e-16 مش مشكلة لأن الأرقام نفسها تقريبية. كمان في مؤشرات الأداء زي response time بالمللي ثانية، الفرق بين 12.3ms و12.300000001ms مالوش أي معنى عملي.

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

افتح أي file في المشروع بتاعك بيحسب فلوس أو ضرائب. دوّر على أي * 0. أو + 0.. كل موضع فيه حساب فلوس بـ float دلوقتي هو bug مؤجّل. حوّله لـ integer cents أو Decimal قبل ما يبان في تقرير محاسبي. لو لاقيت حالة مش متأكد منها، ابعتلي الكود.

المصادر

  • 0.30000000000000004.com — موقع يوضّح نفس السلوك في أكثر من 30 لغة برمجة.
  • Python Docs — Floating Point Arithmetic: Issues and Limitations
  • MDN — Number.EPSILON
  • IEEE 754-2019 Standard for Floating-Point Arithmetic
  • David Goldberg — What Every Computer Scientist Should Know About Floating-Point Arithmetic
  • Stripe Docs — Zero-decimal currencies and integer cents convention

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

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

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