اطبع 0.1 + 0.2 في معظم لغات البرمجة وستحصل على 0.30000000000000004. لا شيء معطل؛ فهذه طريقة الحواسيب في تخزين الأعداد الحقيقية. يشرح هذا الدليل صيغة الدقة المضاعفة IEEE 754 التي يستخدمها SciKey ومعظم البرمجيات العددية، وكيف تظهر حدودها في النتائج، وكيف تتعامل الآلة الحاسبة معها.
1. الدقة المضاعفة IEEE 754
يستخدم العدد المضاعف الدقة (binary64) عدد 64 بتًا.
| الجزء | البتات | الدور |
|---|---|---|
| الإشارة | 1 | موجب أو سالب |
| الأس | 11 | أي قوة للعدد 2 |
| الكسر (الجزء المعنوي) | 52 | الأرقام المعنوية (53 فعليًا، مع 1 بادئ مخفي) |
تقابل البتات المعنوية الـ 53 نحو 15 إلى 17 رقمًا عشريًا. وأكبر قيمة يمكن تمثيلها نحو 1.797×10³⁰⁸، والفجوة بين 1 والعدد المضاعف التالي له (إبسيلون الآلة) هي 2⁻⁵² ≈ 2.22×10⁻¹⁶.
2. العدد 0.1 ليس له تمثيل ثنائي دقيق
كما أن 1/3 = 0.333… لا ينتهي في النظام العشري، فإن 1/10 لا ينتهي في النظام الثنائي: 0.1 = 0.0001100110011…₂ يتكرر بلا نهاية. ويُقرَّب إلى 53 بتًا، فتكون القيمة المخزنة فعليًا
0.1000000000000000055511151231257827…
والعدد 0.2 يُخزَّن أيضًا أكبر قليلًا من حقيقته. ويحمل مجموعهما كلا الخطأين ليصبح 0.3000000000000000444…، وهو عدد مضاعف مختلف عن العدد الذي يخزّن 0.3 (0.2999999999999999888…). ولذلك فإن أقصر عدد عشري يميّز هذا المجموع هو 0.30000000000000004.
وللسبب نفسه فإن 0.1×3 تساوي أيضًا 0.30000000000000004. أما الكسور العشرية التي مقاماتها قوى للعدد 2، مثل 0.5 و0.25 و0.375، فدقيقة في النظام الثنائي ولا تحمل أي خطأ.
3. لماذا تعرض الآلة الحاسبة 0.3 مع ذلك
لا يعرض SciKey القيمة الداخلية الخام؛ بل يقرّبها وفق إعداد الأرقام المعنوية لديك (12 افتراضيًا). عند التقريب إلى 12 رقمًا تصبح 0.30000000000000004 هي 0.300000000000، أي 0.3. فالخطأ يقع في الرقم السابع عشر ولا يصل إلى الشاشة أبدًا.
والآلات الحاسبة العلمية المحمولة تعمل بالطريقة نفسها: فهي عادةً تحسب بنحو 15 رقمًا داخليًا وتعرض 10، فتُخفي الأخطاء الضئيلة في الأرقام الأخيرة في مرحلة العرض. يتغير العرض فقط، لذا تبقى القيمة المخزنة 0.30000000000000004. وعند استخدامها في حسابات لاحقة تبقى الأخطاء عادةً في حدود 10⁻¹⁶، وهي بلا أهمية عمليًا.
4. لماذا لا يساوي sin(π) صفرًا
في وضع RAD ينبغي أن يساوي sin(π) صفرًا تمامًا. لكن π عدد غير نسبي، وأقرب عدد مضاعف الدقة إلى π أصغر من القيمة الحقيقية بنحو 1.22×10⁻¹⁶. وقرب π يكون ميل دالة الجيب −1، لذا فإن sin(π المضاعفة) ≈ 1.2246×10⁻¹⁶. لقد حُسب الجيب نفسه بدقة؛ لكن المدخل لم يكن π تمامًا قط.
يصحّح SciKey ذلك بالقواعد التالية:
- في وضعي DEG وGRAD يُختزل الوسيط اختزالًا دقيقًا بمقدار 360° (400 gon)، وتعيد مضاعفات 90° قيمًا دقيقة:
sin(180)= 0، وcos(90)= 0، وtan(90)غير معرَّف فيعيد خطأ. - في وضع RAD، إذا طابق الوسيط مضاعفًا صحيحًا لـ π ضمن خطأ نسبي 10⁻¹³ تعيد sin القيمة 0؛ وإذا طابق مضاعفًا فرديًا لـ π/2 تعيد cos القيمة 0 وتعيد tan خطأ.
لذا تعرض sin(π) في SciKey القيمة 0. ولا يُطبَّق هذا التصحيح إلا قرب مضاعفات π، ولا يؤثر في أي حساب آخر.
5. الأعداد الكبيرة: المضروب والفيض
ينمو المضروب بسرعة. فالعدد 170! ≈ 7.25741561531×10³⁰⁶ لا يزال يتسع في العدد المضاعف الدقة، لكن 171! يساوي نحو 1.24×10³⁰⁹، متجاوزًا الحد الأقصى 1.797×10³⁰⁸. ولهذا تقبل n! في SciKey الأعداد الصحيحة من 0 إلى 170، وتعيد 171! خطأ فيض.
عمليًا تظهر المضروبات الكبيرة عادةً داخل نسب. فالتوافيق C(200, 3) تساوي 200!/(3!·197!)، لكن nCr(200, 3) = 1313400 تحسبها مباشرة دون قيم وسيطة ضخمة. وفي الاحتمالات، أخذ اللوغاريتمات وجمعها أسلوب شائع آخر.
6. حد الأعداد الصحيحة: 2⁵³
مع 53 بتًا معنويًا يُخزَّن كل عدد صحيح حتى 2⁵³ = 9007199254740992 بدقة تامة. وبعد ذلك تتخطى الأعداد المضاعفة بعض الأعداد الصحيحة. فالعدد 2⁵³ + 1 لا يمكن تمثيله ويُقرَّب عائدًا إلى 2⁵³، لذا فإن 2^53+1-2^53 تعطي 0 لا 1: فالواحد المضاف اختفى عند تخزين المجموع.
ولهذا يعرض SciKey الصيغ الثنائية والثمانية والست عشرية فقط للأعداد الصحيحة التي لا تتجاوز قيمتها المطلقة 2⁵³؛ فما بعد ذلك لا تُضمن صحة الخانات الدنيا. وهذا أيضًا سبب تغيّر الأرقام الأخيرة لأرقام البطاقات أو المعرّفات التي تزيد على 16 رقمًا عند معاملتها كأعداد.
7. الإلغاء الكارثي: أخطر الأخطاء
طرح عددين متقاربين جدًا يمحو الأرقام الأولى ولا يُبقي سوى الخطأ في الغالب. ويُسمى ذلك الإلغاء الكارثي (catastrophic cancellation).
المثال 1. ينبغي أن يساوي (1+1E-15)-1 القيمة 10⁻¹⁵، لكن الدقة المضاعفة تعطي 1.11022302463×10⁻¹⁵. فتخزين 1 + 10⁻¹⁵ يُدخل خطأ تقريب من رتبة 10⁻¹⁶، وبعد طرح 1 يصبح ذلك الخطأ 11% من النتيجة.
المثال 2. عندما x = 10⁸ تكون √(x²+1) − x نحو 5×10⁻⁹. وعند الحساب المباشر لا يمكن تمييز √(10¹⁶+1) عن 10⁸ فتكون النتيجة 0. والتخلص من الجذر في البسط (الضرب في المرافق) يعطي صيغة مكافئة بلا طرح:
√(x²+1) − x = 1 / (√(x²+1) + x)
خزّن 10⁸ في x، فتعيد 1/(sqrt(x^2+1)+x) القيمة 5×10⁻⁹ بدقة.
المثال 3. قيمة 1 − cos(10⁻⁸) نحو 5×10⁻¹⁷، لكن cos(10⁻⁸) تُقرَّب إلى 1 تمامًا فتكون النتيجة المباشرة 0. وباستخدام المتطابقة 1 − cos x = 2 sin²(x/2) تعطي 2sin(0.5E-8)^2 القيمة 5×10⁻¹⁷ (في وضع RAD).
لا يصلح أي عدد من أرقام العرض هذه المشكلة. عليك إعادة كتابة الصيغة.
8. نصائح عملية
- لا تختبر التساوي التام. لمقارنة نتيجتين محسوبتين تحقق مما إذا كان الفرق بينهما صغيرًا بما يكفي (مثلًا خطأ نسبي 10⁻¹²).
- تعامل مع المبالغ المالية بوحدات صحيحة. احسب بأصغر وحدة للعملة كعدد صحيح كي لا تتراكم أخطاء الكسور.
- تجنب طرح الأعداد المتقاربة. استخدم الضرب في المرافق أو المتطابقات أو أعد ترتيب الصيغة لإزالة الطرح.
- استخدم النسب مع الأعداد الكبيرة. عندما تنفجر المضروبات أو القوى، استخدم nCr أو اللوغاريتمات أو اختصر الحدود أولًا.
- لا تقرّب النتائج الوسيطة. خطأ الدقة المضاعفة في حدود 10⁻¹⁶، أما التقريب اليدوي إلى أربعة أرقام فيُدخل أخطاء في حدود 10⁻⁴. والأخطاء الأكبر مصدرها البشر عادةً لا الآلة.
- جرّب رفع العرض إلى 15 رقمًا. إذا بدت نتيجة غريبة، فاعرض مزيدًا من الأرقام لترى القيمة الداخلية وتميّز خطأ الأرقام الأخيرة من صيغة خاطئة فعلًا.