ملخص
ليست الأعداد ذات المنازل العشرية في البرنامج هي الأعداد الحقيقية في الرياضيات: إنها مجموعة جزئية منتهية من الأعداد النسبية التي مقامها قوة للعدد اثنين، وكل قيمة لا تنتمي إلى هذه المجموعة الجزئية تُستبدل بأقرب قيمة تنتمي إليها. يدرس هذا الدرس كيف يُحفظ عدد الفاصلة العائمة في 64 بتًا (الإشارة والأس والجزء المعنوي)، ولماذا لا يملك تمثيلًا دقيقًا وما القيمة التي تحلّ محلّه، وكم رقمًا مأمونًا في float وفي double، وكيف تتراكم أخطاء التقريب، ولماذا لا يُقارن عددان حقيقيان بـ == وكيف يُقارنان بهامش تسامح مُعلَّل، وما اللانهاية والقيمة «ليس عددًا» اللتان تحجزهما الصيغة للنتائج التي لا تتّسع لها أو لا وجود لها.
أهداف التعلم
- شرح لماذا لا يملك تمثيلًا ثنائيًا دقيقًا وما القيمة التي تُحفظ مكانه.
- التنبّؤ بأثر التقريب في الجموع المتكررة وفي جمع أعداد ذات مقادير شديدة التفاوت.
- مقارنة الأعداد الحقيقية بهامش تسامح مناسب بدلًا من
==، مع تعليل اختيار الهامش. - الاختيار بين
floatوdoubleبحسب الدقة المطلوبة، وتعرّف اللانهاية والقيمة «ليس عددًا».
الصيغة العلمية في الأساس 2
بيّن الدرس الأعداد الصحيحة وتمثيلها أن العدد الصحيح ذا بت يمثّل بالضبط قيمة متتالية. غير أن الأعداد ذات المنازل العشرية تطرح صعوبة مختلفة: فبين أي عددين حقيقيين عدد لانهائي من الأعداد الأخرى، ولا يستطيع أي عدد منتهٍ من البتات أن يميّزها جميعًا. والحل الذي تعتمده جميع الحواسيب الحالية هو نفسه الذي يستعمله العلم لكتابة المقادير الكبيرة جدًا أو الصغيرة جدًا: الصيغة العلمية. ففي الأساس 10 تُكتب المسافة المتوسطة بين الأرض والشمس مترًا؛ وللعدد إشارة، وبضعة أرقام معنوية (الجزء المعنوي، )، وقوة للأساس (الأس، ) تدلّ على موضع الفاصلة. وإذا كان الأمر كذلك، فإن عددًا ثابتًا من الأرقام يغطّي مقادير من رتب شديدة التفاوت، مقابل ألّا يُحفظ من كل مقدار إلا أرقامه الأولى.
والحاسوب يفعل الشيء نفسه في الأساس 2. فكل عدد غير الصفر يُكتب بطريقة وحيدة على الصورة
حيث هي الإشارة (0 للموجب، و1 للسالب)، و هو الجزء المعنوي مكتوبًا بالنظام الثنائي، ورقمه الأول دائمًا 1، و هو الأس. ولكون الرقم الأول دائمًا 1 نتيجة عملية: لا حاجة إلى حفظه، والبت الذي يُوفَّر يُكسب للدقة. واسم الفاصلة العائمة يصف هذه الصورة تحديدًا: فالفاصلة لا تشغل موضعًا ثابتًا بين الأرقام، بل «تعوم» بحسب الأس.
يستعمل النوع double في C++، في جميع المترجمات الحالية، الصيغة الثنائية مزدوجة الدقة الموحّدة دوليًا، ذات 64 بتًا: بت واحد للإشارة، و11 بتًا للأس، و52 بتًا للجزء المعنوي، من غير الرقم الأول. ويُحفظ الأس مُزاحًا: فحقل البتات الأحد عشر يحتوي ، بحيث يحفظ قيمًا عديمة الإشارة من 1 إلى 2046 لأسس من إلى (وقيمتا الحقل 0 و2047 محجوزتان للصفر، والأعداد الصغيرة جدًا، واللانهاية، والقيمة «ليس عددًا»، وسنراها في آخر الدرس). أما النوع float فيستعمل صيغة الدقة الأحادية، ذات 32 بتًا: بت واحد للإشارة، و8 للأس، و23 للجزء المعنوي.
ويمكن رؤية بتات double. فالبرنامج الآتي يستعمل std::bit_cast من ملف الترويسة <bit>، التي تعيد تفسير بتات العدد الأربعة والستين عددًا صحيحًا عديم الإشارة من 64 بتًا، من غير أن تغيّر أيًا منها؛ وهي تُستعمل هنا من غير مزيد شرح، أداةً للنظر في الذاكرة. ومواصفة التنسيق {:064b} تكتب ذلك العدد الصحيح بالنظام الثنائي في 64 رقمًا، كما في الدرس الماضي، و{:016X} بالنظام الست عشري في 16.
#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يجمع الشكل البتات الأربعة والستين التي كتبها البرنامج ويقرؤها حقلًا حقلًا.
البرهان
البتات الأربعة والستون بالنظام الست عشري:
3FB999999999999A. وتُقرأ من اليسار إلى اليمين: بت واحد للإشارة، و11 للأس، و52 للجزء المعنوي.الإشارة 0: العدد موجب.
حقل الأس، بالنظام الثنائي، قيمته 1019؛ ويُطرح منه الإزاحة، 1023، فيكون الأس .
الجزء المعنوي، مع الواحد الأول الذي لا يُحفظ، هو بالنظام الثنائي: تتكرّر المجموعة ، والمجموعة الأخيرة، ، مقرَّبة إلى الأعلى. وقيمته تقريبًا .
العدد المحفوظ هو ، أي : قريب جدًا من ، لكنه ليس .
جارٍ تحميل المحتوى…