أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالمناهج والباقات
أحمد حايس

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

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

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

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • المناهج والباقات
  • المدونة

الدعم

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

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

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

ليه 0.1 + 0.2 مش بيساوي 0.3 في كل لغات البرمجة (Floating Point للمتوسط)

متوسط١٩ يونيو ٢٠٢٦4 دقائق قراءة
ليه 0.1 + 0.2 مش بيساوي 0.3 في كل لغات البرمجة (Floating Point للمتوسط)

مستوى المقال: متوسط — مكتوب للمطوّر اللي بيكتب كود بيلمس أرقام أو فلوس.

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

لو جمعت 0.1 + 0.2 في بايثون أو جافاسكريبت ولقيت الناتج 0.30000000000000004، ده مش bug في لغتك ولا عطل في معالجك. ده سلوك متعمّد ومضبوط 100%. في آخر المقال هتعرف ليه بيحصل، وتقدر تمنعه يكسر حساب فلوس بسطر أو اتنين.

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

تخيّل عربة شراء فيها 3 منتجات سعر كل واحد 0.10 دولار. المفروض المجموع 0.30. لكن الكود بيقولك المجموع 0.30000000000000004، وبوابة الدفع بترفض العملية لأن الرقم مش مطابق. المشكلة دي بتظهر فجأة في الفلوس، والـ unit test اللي مكتوب فيه assert total == 0.3 بيفشل وانت مش فاهم ليه.

Python
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False

الحكاية ببساطة الأول

قبل أي كلام علمي، خد المثال ده. جرّب تكتب ثلث (1 ÷ 3) في النظام العشري اللي بنعدّ بيه: 0.3333 وهكذا. كل ما تزوّد تلاتات عمرك ما توصل لثلث بالظبط، لأنه كسر متكرر لا نهائي عندنا.

الكمبيوتر بيحسب بنظام تاني خالص: النظام الثنائي، أرقامه 0 و1 بس. وفيه كسور سهلة جدًا عندنا — زي 0.1 — بتطلع كسور متكررة لا نهائية عنده، بالظبط زي ما 1/3 لا نهائي عندنا. فالكمبيوتر بيخزّن أقرب رقم يقدر عليه، والفرق الصغير ده هو اللي بيطلع أول ما تجمع.

اللي بيحصل فعلاً جوّه IEEE 754

دلوقتي بالتفاصيل. أغلب اللغات بتخزّن الكسور العشرية في نوع اسمه double بمعيار IEEE 754، وحجمه 64 بت متقسّمين كالتالي: بت واحد للإشارة، 11 بت للأس (exponent)، و52 بت للجزء الكسري (mantissa).

مخطط ترتيب بتات الـ double في IEEE 754: بت إشارة واحد، و11 بت للأس، و52 بت للكسر (mantissa)

الرقم 0.1 في النظام الثنائي بيساوي 0.0001100110011 وهكذا، والكتلة «0011» بتتكرر للأبد. بما إن عندك 52 بت بس تخزّن فيهم الكسر، المعالج بيقرّب لأقرب قيمة ممكنة. القيمة المخزّنة لـ 0.1 أكبر شوية من 0.1 الحقيقي، ولـ 0.2 كمان، فلما تجمعهم بيتراكم خطأ في حدود 5.5×10⁻¹⁷ ويظهر في آخر الأرقام.

الافتراض هنا إنك بتستخدم double، وهو الافتراضي في JavaScript و Python وأغلب الـ JSON. الـ double بيديك دقة 15 إلى 17 رقم عشري معنوي تقريبًا — كفاية لأغلب الحسابات، لكنه مش مضبوط بالمللي للكسور العشرية زي الفلوس.

الحل العملي: 3 طرق وكل واحدة بتمنها

ركّز على مبدأين: متقارنش أرقام عشرية بـ ==، ومتخزّنش فلوس في float أو double.

  1. احسب بالأعداد الصحيحة (أصغر وحدة). خزّن الفلوس بالقرش/السنت كأرقام صحيحة، والحساب بيطلع مضبوط 100%.
  2. استخدم Decimal. النوع ده بيحسب بنظام عشري حقيقي، فـ 0.1 + 0.2 بتطلع 0.3 بالظبط — بشرط تبعت قيمك كنص مش float.
  3. قارن بسماحية (tolerance). لو مش فلوس، استخدم isclose بدل ==.
Python
# 1) الأعداد الصحيحة: الأأمن للفلوس
cart = [10, 20, 70]          # قروش
total = sum(cart)            # 100 قرش = 1.00 بالظبط
print(total / 100)          # 1.0

# 2) Decimal: لاحظ النصوص "0.1" مش 0.1
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))   # 0.3

# 3) المقارنة بسماحية بدل ==
import math
print(math.isclose(0.1 + 0.2, 0.3))      # True

وفي JavaScript نفس المبدأ:

JavaScript
(0.1 + 0.2).toFixed(2);                       // "0.30"
Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON; // true

الـ trade-off هنا واضح: Decimal مضبوط لكنه أبطأ بمراحل من float (ممكن يوصل عشرات الأضعاف في الحسابات الكتيرة) وبياخد ذاكرة أكتر. طريقة الأعداد الصحيحة أسرع وأأمن، بس بتطلب انضباط إنك تفضل شغّال بالقرش في كل الكود وتحوّل للعرض بس في الآخر. والمقارنة بسماحية سهلة، لكنها مش حل للفلوس لأنها بتقبل فرق بسيط غير مقبول محاسبيًا.

float ولا double؟

فيه نوع أصغر اسمه float أحادي الدقة (32 بت): بت إشارة، 8 بت أس، 23 بت كسر. دقته أقل بكتير (6 إلى 7 أرقام معنوية بس)، فالخطأ بيظهر أسرع وأوضح.

مخطط ترتيب بتات الـ float أحادي الدقة في IEEE 754: بت إشارة، و8 بت أس، و23 بت كسر، أصغر من الـ double

القاعدة: استخدم double (الافتراضي) لأغلب الحسابات العامة. الـ float بيوفّر نص الذاكرة ومفيد في الرسوميات وتعلّم الآلة على ملايين القيم، بس متستخدموش لأي حاجة محتاجة دقة. ولا الاتنين مناسبين للفلوس — استخدم decimal أو أعداد صحيحة.

متى متشغلش بالك بالموضوع ده

لو شغلك مش فلوس ولا مقارنات دقيقة — زي نِسَب، أوزان تقريبية، إحداثيات رسم، أو متوسطات إحصائية — الخطأ في حدود 10⁻¹⁶ مالوش أي تأثير عملي، وتعقيد الكود بـ Decimal هيبقى مبالغة. سيب الـ double وامشي. الموضوع بيهمّك بس لما تقارن بـ == أو تخزّن مبالغ مالية.

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

دوّر في كودك على أي == بين أرقام عشرية، وأي مكان بتخزّن فيه فلوس في float أو double. غيّر المقارنة لـ isclose، وحوّل تخزين الفلوس لأعداد صحيحة بالقرش أو لـ Decimal. لو الـ unit test اللي كان بيفشل على == 0.3 عدّى بعد التغيير، يبقى المشكلة اتقفلت.

المصادر

  • Python Docs — Floating Point Arithmetic: Issues and Limitations
  • IEEE 754 — Wikipedia
  • Double-precision floating-point format — Wikipedia
  • MDN — Number (JavaScript)
  • David Goldberg — What Every Computer Scientist Should Know About Floating-Point Arithmetic
  • 0.30000000000000004.com — نفس الناتج في كل اللغات

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

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

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