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

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

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

المنصة

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

الدعم

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

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

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

ليه 0.1 + 0.2 مش بيساوي 0.3؟ سر الأرقام العشرية في الكمبيوتر

مبتدئ6 أغسطس 20265 دقائق قراءة
ليه 0.1 + 0.2 مش بيساوي 0.3؟ سر الأرقام العشرية في الكمبيوتر

هذا المقال يتطلب مستوى: مبتدئ

ليه 0.1 + 0.2 مش بيساوي 0.3؟

لو فتحت أي لغة برمجة وكتبت 0.1 + 0.2 وطلعلك 0.30000000000000004، اطمن: ده مش باج في اللغة ولا في جهازك. بعد ما تخلص القراءة هتعرف بالظبط ليه بيحصل، وأهم من كده هتعرف تكتب كود مايقعش في الفخ ده وانت بتتعامل مع فلوس أو مقارنات.

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

الكمبيوتر مش بيخزّن الأرقام العشرية زي ما انت كاتبها بالظبط. بيخزّنها بصيغة تقريبية. التقريب ده صغير جدًا، لكنه بيظهر لما تجمع أو تقارن، فتلاقي نتيجة غريبة زي 0.30000000000000004. المشكلة دي موجودة في بايثون وجافاسكريبت وC++ وجافا وأي لغة بتستخدم نظام الأرقام العشرية المعياري.

مثال بسيط قبل ما ندخل في التقنية

خلّينا نبعد عن الكمبيوتر شوية. جرّب تقسم 1 على 3 بإيدك في النظام العشري اللي احنا بنستخدمه. هتطلع 0.3333333... والتلاتات مش بتخلص. لو حبيت تكتبها على ورقة لازم توقف عند حد معيّن، يعني هتكتب رقم تقريبي مش دقيق 100%.

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

اللي بيحصل فعلاً جوه الكمبيوتر

رسم يوضح تحول الكسر العشري 0.1 إلى كسر ثنائي دوري 0.00011... تتكرر فيه المجموعة 1001 إلى ما لا نهاية

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

الرقم 0.1 في الثنائي = 0.0001100110011... والمجموعة 1001 بتفضل تتكرر للأبد. الكمبيوتر بياخد أول 52 بت بس ويرمي الباقي. فالقيمة اللي اتخزنت فعلًا مش 0.1 بالظبط، دي حاجة زي 0.1000000000000000055511151231257827. نفس الكلام بيحصل مع 0.2. لما تجمع القيمتين التقريبيتين، الخطأ الصغير بيتراكم ويظهر في النتيجة على شكل 0.30000000000000004.

الافتراض هنا إن اللغة بتستخدم double بدقة 64 بت، وده الوضع الافتراضي في بايثون وجافاسكريبت. الدقة الفعلية بتوصل لحوالي 15 إلى 17 رقم عشري معنوي، والخطأ في حدود 1e-16 تقريبًا.

جرّب الكود ده بنفسك في بايثون وشوف بعينك:

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

# والخطأ بيتراكم لو جمعت كتير
total = 0.0
for _ in range(10):
    total += 0.1
print(total)        # 0.9999999999999999
print(total == 1.0) # False

سيناريو واقعي: ليه ده بيأذّي فعلاً

تخيّل إن عندك متجر إلكتروني، والعربة فيها 10 منتجات سعر كل واحد 0.10 دولار. المفروض المجموع 1.00 دولار. لكن لو جمعتهم بأرقام float عادية، الناتج هيطلع 0.9999999999999999. لو عندك شرط في الكود بيقول if total == 1.0 علشان تدّي خصم أو تقفل الفاتورة، الشرط هيفشل، والعميل ممكن يتحاسب غلط بفرق سنت.

الفرق سنت واحد يبان تافه، بس لو المتجر بيعمل 100 ألف عملية في اليوم، وكل عملية فيها احتمال انحراف بسيط، بتبقى عندك مشكلة محاسبية حقيقية بتظهر في التقارير آخر الشهر. ده السبب إن أنظمة الفلوس الجدّية مابتخزّنش المبالغ كـ float أبدًا.

الحل الصح

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

  1. لو بتقارن أرقام كسرية: ماتستخدمش ==، استخدم مقارنة بهامش خطأ.
  2. لو بتتعامل مع فلوس: استخدم نوع Decimal اللي بيخزّن الأرقام العشرية بدقة كاملة.
  3. أبسط حيلة للفلوس: اشتغل بالسنتات كأعداد صحيحة (integers) وقسّم على 100 بس وقت العرض.
Python
from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2'))   # 0.3  بالظبط

# أو قارن بهامش خطأ بدل ==
import math
print(math.isclose(0.1 + 0.2, 0.3))      # True

# أو اشتغل بالسنتات كأعداد صحيحة
cents = 10 + 20
print(cents / 100)                        # 0.3

ملاحظة مهمة: لاحظ إننا كتبنا Decimal('0.1') كنص بين علامتين، مش Decimal(0.1). لو مررت 0.1 كرقم، هتكون بعتّه القيمة التقريبية الغلط من الأول، فاكتبه كنص علشان يفضل دقيق.

الـ trade-off

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

متى لا تشغل بالك

لو شغلك في الرسوميات، الألعاب، الحسابات العلمية التقريبية، أو تعلّم الآلة، الخطأ الميكروسكوبي ده مالوش أي تأثير عملي، وممكن تتجاهله تمامًا وتفضل شغّال بـ float عادي علشان السرعة. المشكلة بتبقى خطيرة بس في حالتين: التعامل مع الفلوس، وأي مقارنة == مباشرة بين أرقام كسرية.

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

افتح أقرب مشروع عندك، ودوّر على أي == بين أرقام عشرية، وبدّلها بـ math.isclose. ولو في أي مكان بتتعامل فيه مع مبالغ مالية بـ float، حوّلها دلوقتي لـ Decimal أو للسنتات كأعداد صحيحة. جرّب وشوف الفرق في النتيجة قبل وبعد.

مصادر

  • Python Docs — Floating Point Arithmetic: Issues and Limitations: docs.python.org/3/tutorial/floatingpoint.html
  • IEEE 754 Standard for Floating-Point Arithmetic (IEEE).
  • David Goldberg — What Every Computer Scientist Should Know About Floating-Point Arithmetic (ACM Computing Surveys, 1991).
  • Python Docs — decimal module: docs.python.org/3/library/decimal.html
  • PEP 485 — math.isclose: peps.python.org/pep-0485/

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

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

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