انتقل إلى المحتوى
Topos Uranos

ملخص

ليست الأعداد ذات المنازل العشرية في البرنامج هي الأعداد الحقيقية في الرياضيات: إنها مجموعة جزئية منتهية من الأعداد النسبية التي مقامها قوة للعدد اثنين، وكل قيمة لا تنتمي إلى هذه المجموعة الجزئية تُستبدل بأقرب قيمة تنتمي إليها. يدرس هذا الدرس كيف يُحفظ عدد الفاصلة العائمة في 64 بتًا (الإشارة والأس والجزء المعنوي)، ولماذا لا يملك 0.10.1 تمثيلًا دقيقًا وما القيمة التي تحلّ محلّه، وكم رقمًا مأمونًا في float وفي double، وكيف تتراكم أخطاء التقريب، ولماذا لا يُقارن عددان حقيقيان بـ == وكيف يُقارنان بهامش تسامح مُعلَّل، وما اللانهاية والقيمة «ليس عددًا» اللتان تحجزهما الصيغة للنتائج التي لا تتّسع لها أو لا وجود لها.

أهداف التعلم

  1. شرح لماذا لا يملك 0.10.1 تمثيلًا ثنائيًا دقيقًا وما القيمة التي تُحفظ مكانه.
  2. التنبّؤ بأثر التقريب في الجموع المتكررة وفي جمع أعداد ذات مقادير شديدة التفاوت.
  3. مقارنة الأعداد الحقيقية بهامش تسامح مناسب بدلًا من ==، مع تعليل اختيار الهامش.
  4. الاختيار بين float وdouble بحسب الدقة المطلوبة، وتعرّف اللانهاية والقيمة «ليس عددًا».

الصيغة العلمية في الأساس 2

بيّن الدرس الأعداد الصحيحة وتمثيلها أن العدد الصحيح ذا nn بت يمثّل بالضبط 2n2^n قيمة متتالية. غير أن الأعداد ذات المنازل العشرية تطرح صعوبة مختلفة: فبين أي عددين حقيقيين عدد لانهائي من الأعداد الأخرى، ولا يستطيع أي عدد منتهٍ من البتات أن يميّزها جميعًا. والحل الذي تعتمده جميع الحواسيب الحالية هو نفسه الذي يستعمله العلم لكتابة المقادير الكبيرة جدًا أو الصغيرة جدًا: الصيغة العلمية. ففي الأساس 10 تُكتب المسافة المتوسطة بين الأرض والشمس 1.496×10111.496 \times 10^{11} مترًا؛ وللعدد إشارة، وبضعة أرقام معنوية (الجزء المعنوي، 1.4961.496)، وقوة للأساس (الأس، 1111) تدلّ على موضع الفاصلة. وإذا كان الأمر كذلك، فإن عددًا ثابتًا من الأرقام يغطّي مقادير من رتب شديدة التفاوت، مقابل ألّا يُحفظ من كل مقدار إلا أرقامه الأولى.

والحاسوب يفعل الشيء نفسه في الأساس 2. فكل عدد غير الصفر يُكتب بطريقة وحيدة على الصورة

x=(−1)s×1.m1m2m3…m52 (2)×2ex = (-1)^{s} \times 1.m_1 m_2 m_3 \ldots m_{52} \, {}_{(2)} \times 2^{e}

حيث ss هي الإشارة (0 للموجب، و1 للسالب)، و1.m1m2…1.m_1 m_2 \ldots هو الجزء المعنوي مكتوبًا بالنظام الثنائي، ورقمه الأول دائمًا 1، وee هو الأس. ولكون الرقم الأول دائمًا 1 نتيجة عملية: لا حاجة إلى حفظه، والبت الذي يُوفَّر يُكسب للدقة. واسم الفاصلة العائمة يصف هذه الصورة تحديدًا: فالفاصلة لا تشغل موضعًا ثابتًا بين الأرقام، بل «تعوم» بحسب الأس.

يستعمل النوع double في C++، في جميع المترجمات الحالية، الصيغة الثنائية مزدوجة الدقة الموحّدة دوليًا، ذات 64 بتًا: بت واحد للإشارة، و11 بتًا للأس، و52 بتًا للجزء المعنوي، من غير الرقم الأول. ويُحفظ الأس مُزاحًا: فحقل البتات الأحد عشر يحتوي e+1023e + 1023، بحيث يحفظ قيمًا عديمة الإشارة من 1 إلى 2046 لأسس من −1022-1022 إلى 10231023 (وقيمتا الحقل 0 و2047 محجوزتان للصفر، والأعداد الصغيرة جدًا، واللانهاية، والقيمة «ليس عددًا»، وسنراها في آخر الدرس). أما النوع float فيستعمل صيغة الدقة الأحادية، ذات 32 بتًا: بت واحد للإشارة، و8 للأس، و23 للجزء المعنوي.

ويمكن رؤية بتات double. فالبرنامج الآتي يستعمل std::bit_cast من ملف الترويسة <bit>، التي تعيد تفسير بتات العدد الأربعة والستين عددًا صحيحًا عديم الإشارة من 64 بتًا، من غير أن تغيّر أيًا منها؛ وهي تُستعمل هنا من غير مزيد شرح، أداةً للنظر في الذاكرة. ومواصفة التنسيق {:064b} تكتب ذلك العدد الصحيح بالنظام الثنائي في 64 رقمًا، كما في الدرس الماضي، و{:016X} بالنظام الست عشري في 16.

برنامج بلغة C++
#include <bit>#include <cstdint>#include <print>​int main(){    const double tenth{0.1};    // البتات الأربعة والستون للعدد، مقروءة عددًا صحيحًا عديم الإشارة.    const std::uint64_t bits{std::bit_cast<std::uint64_t>(tenth)};    std::println("{:064b}", bits);    std::println("{:016X}", bits);}
المخرجات
00111111101110011001100110011001100110011001100110011001100110103FB999999999999A
مثالالبتات الأربعة والستون للعدد 0.1

يجمع الشكل البتات الأربعة والستين التي كتبها البرنامج ويقرؤها حقلًا حقلًا.

البرهان

  1. 1+11+52=641 + 11 + 52 = 64

    البتات الأربعة والستون بالنظام الست عشري: 3FB999999999999A. وتُقرأ من اليسار إلى اليمين: بت واحد للإشارة، و11 للأس، و52 للجزء المعنوي.

  2. s=0s = 0

    الإشارة 0: العدد موجب.

  3. e=1019−1023=−4e = 1019 - 1023 = -4

    حقل الأس، 0111111101101111111011 بالنظام الثنائي، قيمته 1019؛ ويُطرح منه الإزاحة، 1023، فيكون الأس −4-4.

  4. m≈1.6000000000000000888m \approx 1.6000000000000000888

    الجزء المعنوي، مع الواحد الأول الذي لا يُحفظ، هو 1.1001100110011…10101.1001100110011\ldots1010 بالنظام الثنائي: تتكرّر المجموعة 10011001، والمجموعة الأخيرة، 10101010، مقرَّبة إلى الأعلى. وقيمته تقريبًا 1.60000000000000008881.6000000000000000888.

  5. x=0.1000000000000000055511…≠0.1x = 0.1000000000000000055511\ldots \neq 0.1

    العدد المحفوظ هو +1.6000000000000000888×2−4+1.6000000000000000888 \times 2^{-4}، أي 0.1000000000000000055511…0.1000000000000000055511\ldots: قريب جدًا من 0.10.1، لكنه ليس 0.10.1.