إذا سألتك ما هو 0.1 + 0.2؟ يمكنك أن تعطيني نظرة فارغة، 0.1 + 0.2 = 0.3 آه، هل مازلت بحاجة إلى السؤال؟ حتى الأطفال في رياض الأطفال يمكنهم الإجابة على سؤال الأطفال هذا. لكن كما تعلمون، نفس المشكلة في لغة البرمجة قد لا تكون بسيطة كما نتصور.
لا تصدق ذلك؟ دعونا نلقي نظرة على قطعة من JS أولاً.
فار نوما = 0.1؛
فار نومب = 0.2؛
تنبيه((numA + numB) === 0.3 );
نتيجة التنفيذ خاطئة. نعم، عندما رأيت هذا الرمز لأول مرة، كنت أعتقد أنه صحيح، لكن نتائج التنفيذ فاجأتني هل طريقة الفتح الخاصة بي خاطئة؟ لا، لا. لنحاول تنفيذ الكود التالي مرة أخرى وسنعرف سبب خطأ النتيجة.
فار نوما = 0.1؛
فار نومب = 0.2؛
تنبيه (numA + numB)؛
اتضح أن 0.1 + 0.2 = 0.30000000000000004. أليس هذا غريبا؟ في الواقع، بالنسبة للعمليات الحسابية الأربع للأرقام الفاصلة العائمة، ستواجه جميع لغات البرمجة تقريبًا مشاكل مشابهة لأخطاء الدقة، ومع ذلك، في لغات مثل C++/C#/Java، تم تغليف الأساليب لتجنب مشاكل الدقة. وجافا سكريبت هو نوع ضعيف، ولا تحتوي اللغة على نوع بيانات صارم لأرقام الفاصلة العائمة من منظور التصميم، لذا فإن مشكلة الخطأ في الدقة بارزة بشكل خاص. دعنا نحلل سبب وجود خطأ الدقة هذا وكيفية إصلاحه.
أولاً، علينا أن نفكر في المشكلة التي تبدو للأطفال وهي 0.1 + 0.2 من منظور الكمبيوتر. نحن نعلم أن ما يمكن قراءته بواسطة أجهزة الكمبيوتر هو ثنائي وليس عشريًا، لذلك دعونا أولاً نحول 0.1 و0.2 إلى ثنائي ونلقي نظرة:
0.1 => 0.0001 1001 1001 1001... (حلقة لا نهائية)
0.2 => 0.0011 0011 0011 0011... (حلقة لا نهائية)
يدعم الجزء العشري لرقم الفاصلة العائمة مزدوج الدقة ما يصل إلى 52 بت، لذلك بعد إضافة الاثنين، نحصل على سلسلة من 0.0100110011001100110011001100110011001100110011001100 يتم اقتطاع الرقم الثنائي بسبب محدودية المنازل العشرية لأرقام الفاصلة العائمة. في هذا الوقت، نقوم بتحويله إلى رقم عشري ويصبح 0.30000000000000004.
هذا كل شيء، فكيف حل هذه المشكلة؟ النتيجة التي أريدها هي 0.1 + 0.2 === 0.3 آه! ! !
أحد أبسط الحلول هو إعطاء متطلبات دقة واضحة أثناء عملية إرجاع القيمة، سيقوم الكمبيوتر بالتقريب تلقائيًا، مثل:
فار نوما = 0.1؛
فار نومب = 0.2؛
تنبيه( parseFloat((numA + numB).toFixed(2)) === 0.3 );
لكن من الواضح أن هذه ليست طريقة نهائية. سيكون أمرًا رائعًا لو كانت هناك طريقة يمكن أن تساعدنا في حل مشكلة دقة أرقام الفاصلة العائمة هذه. دعونا نجرب هذه الطريقة:
Math.formatFloat = وظيفة (و، رقم) {
فار م = Math.pow(10, digit);
إرجاع parseInt(f * m, 10) / m;
}
فار نوما = 0.1؛
فار نومب = 0.2؛
تنبيه(Math.formatFloat(numA + numB, 1) === 0.3);
ماذا تعني هذه الطريقة؟ من أجل تجنب الاختلافات في الدقة، نحتاج إلى ضرب الرقم الذي سيتم حسابه في 10 أس n، وتحويله إلى عدد صحيح يمكن للكمبيوتر التعرف عليه بدقة، ثم قسمته على 10 أس n تتعامل لغات البرمجة مع اختلافات الدقة، وسنستخدمها للتعامل مع خطأ الدقة في أرقام الفاصلة العائمة في JS.
إذا سألك أحد في المرة القادمة ما هو 0.1 + 0.2، يجب أن تكون حذرًا في إجابتك! !