تقريب الأرقام في JavaScript: كيفية الحصول على رقمين عشريين (وليس أكثر)

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

سحر الأرقام: لماذا 0.1 + 0.2 لا يساوي 0.3؟



مرحبًا، أيها خبير JavaScript المستقبلي! إذا حاولت يومًا جمع 0.1 و 0.2 في وحدة تحكم المتصفح، فمن المحتمل أنك تعرضت لصدمة ثقافية:



console.log(0.1 + 0.2); // سيطبع: 0.30000000000000004


نعم، لم يصب JavaScript بالجنون. هذه مشكلة قياسية في حساب الفاصلة العائمة (IEEE 754). ببساطة، لا تستطيع أجهزة الكمبيوتر تمثيل 0.1 بشكل مثالي في النظام الثنائي - إنها مثل محاولة كتابة 1/3 في النظام العشري. النتيجة هي كسر عشري لا نهائي.



وهنا يظهر السؤال الكلاسيكي من Stack Overflow، الذي حصل على آلاف المشاهدات: «How to round to at most 2 decimal places, if necessary». كيف تعرض الرقم بشكل جميل، بدون ذيل من «0000000004»؟ كيف تقرب إلى رقمين عشريين، ولكن فقط إذا لزم الأمر؟



في هذه المقالة، سنشرح دليلًا تعليميًا خطوة بخطوة سيحل المشكلة مرة واحدة وإلى الأبد. ستعرف لماذا toFixed() يكذب أحيانًا، وكيف يعمل Math.round()، وأي طريقة تختار لمتجر إلكتروني، أو آلة حاسبة، أو ببساطة لعرض سعر بشكل جميل.



المشكلة: «أريد رقمين عشريين، ولكن ليس أكثر!»



تخيل أنك تكتب سلة مشتريات. سعر المنتج هو 10.00. تريد عرض 10، وليس 10.00. ولكن إذا كان السعر 10.456، تريد عرض 10.46. المنطق بسيط: نقرب إلى رقمين عشريين، ولكن نتخلص من الأصفار الزائدة.



الطريقة القياسية toFixed(2) تفعل هذا:



const price = 10;console.log(price.toFixed(2)); // "10.00" — لكننا نريد "10"

const price2 = 10.456;console.log(price2.toFixed(2)); // "10.46" — هذا جيد


المشكلة: toFixed() دائمًا ما تُرجع سلسلة نصية ودائمًا تضيف أصفارًا. هذا غير مناسب.



الفارق الدقيق الثاني — toFixed() قد تقرب بشكل غريب بسبب نفس مشكلة الفاصلة العائمة:



console.log((1.005).toFixed(2)); // "1.00" — لكن يجب أن تكون "1.01"!


بينجو! لهذا السبب السؤال على Stack Overflow مشهور جدًا. دعنا نتعرف على الحلول.



الحل رقم 1: التقريب الرياضي (Math.round)



الطريقة الأكثر موثوقية هي ضرب الرقم في 100، تقريبه، ثم قسمته مرة أخرى. تعمل هكذا:



function roundToTwo(num) {  return Math.round(num * 100) / 100;}

console.log(roundToTwo(10.456)); // 10.46console.log(roundToTwo(10)); // 10console.log(roundToTwo(1.005)); // 1 — عفوًا، المشكلة مرة أخرى


أرأيت؟ 1.005 * 100 = 100.49999999999999 (بسبب IEEE 754). Math.round يقرب هذا إلى الأسفل، نحصل على 100، نقسم على 100 = 1. ليس جيدًا.



الخلاصة: الطريقة جيدة للحالات البسيطة، ولكن ليس للحسابات المالية. للأمثلة التعليمية — مقبولة.



الحل رقم 2: Number.EPSILON — منقذ الدقة



للتغلب على خطأ الفاصلة العائمة، يمكننا إضافة دلتا صغيرة جدًا — Number.EPSILON. هذا هو أصغر فرق بين رقمين يمكن لـ JavaScript تمييزه (حوالي 2.2e-16).



function roundToTwo(num) {  return Math.round((num + Number.EPSILON) * 100) / 100;}

console.log(roundToTwo(1.005)); // 1.01 — يعمل!console.log(roundToTwo(10.456)); // 10.46console.log(roundToTwo(10)); // 10


لماذا يعمل هذا؟ نحن ندفع الرقم قليلاً إلى الأعلى، بحيث يتحول 100.4999... إلى 100.5... ويتم تقريبه بشكل صحيح.



مهم: هذه الطريقة ليست مثالية لجميع الحالات الحدودية، ولكن بالنسبة لـ 99% من المهام — إنها حل ممتاز. هذا هو ما يُنصح به غالبًا على Stack Overflow.



الحل رقم 3: toFixed() + Number() — خدعة ذكية



طريقة شائعة أخرى هي استخدام toFixed(2)، ثم تحويل السلسلة النصية مرة أخرى إلى رقم باستخدام Number() أو علامة الجمع الأحادية +. الأصفار الزائدة ستختفي.

المدونات

توصيات الكتب