في جافا سكريبت الحديثة، هناك نوعان من الأرقام:
يتم تخزين الأرقام العادية في JavaScript بتنسيق 64 بت IEEE-754، المعروف أيضًا باسم "أرقام الفاصلة العائمة مزدوجة الدقة". هذه هي الأرقام التي نستخدمها معظم الوقت، وسنتحدث عنها في هذا الفصل.
تمثل أرقام BigInt أعدادًا صحيحة ذات طول عشوائي. تكون هناك حاجة إليها في بعض الأحيان لأن الرقم الصحيح العادي لا يمكن أن يتجاوز بأمان (2 53 -1)
أو يكون أقل من -(2 53 -1)
، كما ذكرنا سابقًا في فصل أنواع البيانات. بما أن Bigints تُستخدم في بعض المجالات الخاصة، فإننا نخصصها لفصل خاص BigInt.
لذلك سنتحدث هنا عن الأعداد العادية. دعونا نوسع معرفتنا بهم.
تخيل أننا بحاجة إلى كتابة مليار. الطريقة الواضحة هي:
دع مليار = 1000000000؛
يمكننا أيضًا استخدام الشرطة السفلية _
كفاصل:
دع مليار = 1_000_000_000؛
هنا تلعب الشرطة السفلية _
دور "السكر النحوي"، فهي تجعل الرقم أكثر قابلية للقراءة. يتجاهل محرك JavaScript ببساطة _
بين الأرقام، لذا فهو بالضبط نفس المليار كما هو مذكور أعلاه.
لكن في الحياة الواقعية، نحاول تجنب كتابة تسلسلات طويلة من الأصفار. نحن كسالى جدا لذلك. سنحاول كتابة شيء مثل "1bn"
مقابل مليار أو "7.3bn"
مقابل 7 مليارات و300 مليون. وينطبق الشيء نفسه على معظم الأعداد الكبيرة.
في جافا سكريبت، يمكننا تقصير رقم من خلال إلحاق الحرف "e"
به وتحديد عدد الأصفار:
دع مليار = 1e9؛ // 1 مليار، حرفيًا: 1 و9 أصفار تنبيه (7.3e9) ؛ // 7.3 مليار (مثل 7300000000 أو 7_300_000_000)
بمعنى آخر، يقوم e
بضرب الرقم في 1
بعدد الأصفار المحدد.
1e3 === 1 * 1000; // e3 يعني *1000 1.23e6 === 1.23 * 1000000; // e6 يعني *1000000
الآن دعونا نكتب شيئا صغيرا جدا. لنفترض أن 1 ميكروثانية (واحد من المليون من الثانية):
دع mсs = 0.000001؛
تمامًا كما كان من قبل، فإن استخدام "e"
يمكن أن يساعد. إذا أردنا تجنب كتابة الأصفار بشكل صريح، يمكننا أن نكتب نفس الشيء على النحو التالي:
دع mcs = 1e-6؛ // خمسة أصفار إلى اليسار من 1
إذا عددنا الأصفار في 0.000001
، فهناك 6 منهم. لذا فمن الطبيعي أن يكون 1e-6
.
بمعنى آخر، الرقم السالب بعد "e"
يعني القسمة على 1 مع عدد الأصفار المحدد:
// -3 تقسم على 1 بثلاثة أصفار 1e-3 === 1/1000; // 0.001 // -6 نقسم على 1 ويكون لدينا 6 أصفار 1.23e-6 === 1.23 / 1000000؛ // 0.00000123 // مثال برقم أكبر 1234e-2 === 1234 / 100; // 12.34، تتحرك العلامة العشرية مرتين
تُستخدم الأرقام السداسية العشرية على نطاق واسع في JavaScript لتمثيل الألوان، وترميز الأحرف، وأشياء أخرى كثيرة. ومن الطبيعي أن توجد طريقة أقصر لكتابتها: 0x
ثم الرقم.
على سبيل المثال:
تنبيه (0xff)؛ // 255 تنبيه (0xFF)؛ // 255 (نفس الحالة، لا يهم)
نادرًا ما يتم استخدام أنظمة الأرقام الثنائية والثمانية، ولكنها مدعومة أيضًا باستخدام البادئات 0b
و 0o
:
دع أ = 0b11111111؛ // شكل ثنائي من 255 دع ب = 0o377؛ // الشكل الثماني للعدد 255 تنبيه (أ == ب)؛ // صحيح، نفس الرقم 255 في كلا الجانبين
لا يوجد سوى 3 أنظمة أرقام مع هذا الدعم. بالنسبة لأنظمة الأرقام الأخرى، يجب علينا استخدام الدالة parseInt
(والتي سنراها لاحقًا في هذا الفصل).
تقوم الطريقة num.toString(base)
بإرجاع تمثيل سلسلة لـ num
في نظام الأرقام مع base
المحددة.
على سبيل المثال:
دع الأعداد = 255؛ تنبيه(num.toString(16)); // وما يليها تنبيه(num.toString(2)); // 11111111
يمكن أن تختلف base
من 2
إلى 36
. بشكل افتراضي، هو 10
.
حالات الاستخدام الشائعة لهذا هي:
يتم استخدام base=16 للألوان السداسية وترميزات الأحرف وما إلى ذلك، ويمكن أن تكون الأرقام 0..9
أو A..F
.
base=2 مخصص في الغالب لتصحيح أخطاء عمليات البت، ويمكن أن تكون الأرقام 0
أو 1
.
base=36 هو الحد الأقصى، ويمكن أن تكون الأرقام 0..9
أو A..Z
. يتم استخدام الأبجدية اللاتينية بأكملها لتمثيل رقم. هناك حالة مضحكة ولكنها مفيدة لـ 36
، وهي عندما نحتاج إلى تحويل معرف رقمي طويل إلى شيء أقصر، على سبيل المثال، لإنشاء عنوان url قصير. يمكن ببساطة تمثيله في نظام الأرقام ذو الأساس 36
:
تنبيه(123456..toString(36)); // 2n9c
نقطتان لاستدعاء الطريقة
يرجى ملاحظة أن النقطتين في 123456..toString(36)
ليسا خطأ مطبعي. إذا أردنا استدعاء عملية مباشرة على رقم، مثل toString
في المثال أعلاه، فنحن بحاجة إلى وضع نقطتين ..
بعده.
إذا وضعنا نقطة واحدة: 123456.toString(36)
، فسيكون هناك خطأ، لأن بناء جملة JavaScript يشير إلى الجزء العشري بعد النقطة الأولى. وإذا وضعنا نقطة أخرى، فإن جافا سكريبت تعرف أن الجزء العشري فارغ ويتبع الآن الطريقة.
يمكن أيضًا كتابة (123456).toString(36)
.
إحدى العمليات الأكثر استخدامًا عند التعامل مع الأرقام هي التقريب.
هناك العديد من الوظائف المضمنة للتقريب:
Math.floor
التقريب للأسفل: 3.1
يصبح 3
و -1.1
يصبح -2
.
Math.ceil
التقريب للأعلى: 3.1
يصبح 4
و -1.1
يصبح -1
.
Math.round
التقريب إلى أقرب عدد صحيح: 3.1
يصبح 3
، 3.6
يصبح 4
. في الحالات المتوسطة 3.5
طلقة حتى 4
و -3.5
طلقة حتى -3
.
Math.trunc
(غير مدعوم من Internet Explorer)
يزيل أي شيء بعد العلامة العشرية دون التقريب: 3.1
يصبح 3
، -1.1
يصبح -1
.
وفيما يلي الجدول لتلخيص الاختلافات بينهما:
Math.floor | Math.ceil | Math.round | Math.trunc | |
---|---|---|---|---|
3.1 | 3 | 4 | 3 | 3 |
3.5 | 3 | 4 | 4 | 3 |
3.6 | 3 | 4 | 4 | 3 |
-1.1 | -2 | -1 | -1 | -1 |
-1.5 | -2 | -1 | -1 | -1 |
-1.6 | -2 | -1 | -2 | -1 |
تغطي هذه الوظائف جميع الطرق الممكنة للتعامل مع الجزء العشري من الرقم. ولكن ماذا لو أردنا تقريب الرقم إلى الرقم n-th
بعد العلامة العشرية؟
على سبيل المثال، لدينا 1.2345
ونريد تقريبه إلى رقمين، لنحصل على 1.23
فقط.
هناك طريقتان للقيام بذلك:
الضرب والقسمة.
على سبيل المثال، لتقريب الرقم إلى الرقم الثاني بعد العلامة العشرية، يمكننا ضرب الرقم في 100
، واستدعاء دالة التقريب ثم تقسيمه مرة أخرى.
دع الأعداد = 1.23456؛ تنبيه( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
تقوم الطريقة toFixed(n) بتقريب الرقم إلى أرقام n
بعد النقطة وإرجاع تمثيل سلسلة للنتيجة.
دع الأعداد = 12.34؛ تنبيه(num.toFixed(1)); // "12.3"
يؤدي هذا إلى التقريب لأعلى أو لأسفل إلى أقرب قيمة، على غرار Math.round
:
دع الأعداد = 12.36؛ تنبيه(num.toFixed(1)); // "12.4"
يرجى ملاحظة أن نتيجة toFixed
هي سلسلة. إذا كان الجزء العشري أقصر مما هو مطلوب، يتم إلحاق الأصفار في النهاية:
دع الأعداد = 12.34؛ تنبيه(num.toFixed(5)); // "12.34000"، تمت إضافة أصفار لتكوين 5 أرقام بالضبط
يمكننا تحويله إلى رقم باستخدام علامة الجمع الأحادية أو استدعاء Number()
، على سبيل المثال، اكتب +num.toFixed(5)
.
داخليًا، يتم تمثيل الرقم بتنسيق 64 بت IEEE-754، لذلك هناك 64 بت بالضبط لتخزين الرقم: 52 منها تستخدم لتخزين الأرقام، و11 منها لتخزين موضع العلامة العشرية، و1 بت هو للعلامة.
إذا كان الرقم ضخمًا بالفعل، فقد يتجاوز سعة تخزين 64 بت ويصبح قيمة رقمية خاصة Infinity
:
تنبيه (1e500) ؛ // إنفينيتي
ما قد يكون أقل وضوحًا، ولكنه يحدث كثيرًا، هو فقدان الدقة.
خذ بعين الاعتبار اختبار المساواة (الزائف!) هذا:
تنبيه( 0.1 + 0.2 == 0.3 ); // خطأ شنيع
هذا صحيح، إذا تحققنا مما إذا كان مجموع 0.1
و 0.2
هو 0.3
، فسنحصل على false
.
غريب! فماذا إذا لم يكن 0.3
؟
تنبيه (0.1 + 0.2)؛ // 0.3000000000000004
أوه! تخيل أنك تنشئ موقعًا للتسوق الإلكتروني ويضع الزائر سلعًا $0.10
و $0.20
في سلة التسوق الخاصة به. سيكون إجمالي الطلب $0.30000000000000004
. من شأنه أن يفاجئ أي شخص.
ولكن لماذا يحدث هذا؟
يتم تخزين الرقم في الذاكرة في شكله الثنائي، وهو عبارة عن سلسلة من البتات - الآحاد والأصفار. لكن الكسور مثل 0.1
و 0.2
التي تبدو بسيطة في النظام الرقمي العشري هي في الواقع كسور لا تنتهي في شكلها الثنائي.
تنبيه(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101 تنبيه(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001101 تنبيه ((0.1 + 0.2).toString(2)); // 0.0100110011001100110011001100110011001100110011001101
ما هو 0.1
؟ وهو واحد مقسوم على عشرة 1/10
، أي عشر. في نظام الأرقام العشرية، يمكن تمثيل هذه الأرقام بسهولة. قارنه بالثلث: 1/3
. يصبح كسرًا لا نهاية له 0.33333(3)
.
لذا، فإن القسمة على القوى 10
مضمونة للعمل بشكل جيد في النظام العشري، ولكن القسمة على 3
ليست كذلك. لنفس السبب، في نظام الأرقام الثنائية، يتم ضمان نجاح القسمة على قوى 2
، لكن 1/10
يصبح جزءًا ثنائيًا لا نهاية له.
لا توجد طريقة لتخزين 0.1 أو 0.2 بالضبط باستخدام النظام الثنائي، تمامًا كما لا توجد طريقة لتخزين الثلث ككسر عشري.
يحل التنسيق الرقمي IEEE-754 هذه المشكلة عن طريق التقريب إلى أقرب رقم ممكن. عادة لا تسمح لنا قواعد التقريب هذه برؤية "الخسارة الصغيرة في الدقة"، ولكنها موجودة.
يمكننا أن نرى هذا على أرض الواقع:
تنبيه( 0.1.toFixed(20) ); // 0.1000000000000000555
وعندما نجمع رقمين، فإن "خسائر الدقة" تتراكم.
لهذا السبب 0.1 + 0.2
ليس بالضبط 0.3
.
ليس فقط جافا سكريبت
نفس المشكلة موجودة في العديد من لغات البرمجة الأخرى.
تعطي PHP وJava وC وPerl وRuby نفس النتيجة تمامًا، لأنها تعتمد على نفس التنسيق الرقمي.
هل يمكننا حل المشكلة؟ من المؤكد أن الطريقة الأكثر موثوقية هي تقريب النتيجة بمساعدة الطريقة toFixed(n):
دع المبلغ = 0.1 + 0.2؛ تنبيه(sum.toFixed(2)); // "0.30"
يرجى ملاحظة أن toFixed
يُرجع دائمًا سلسلة. يضمن أنه يحتوي على رقمين بعد العلامة العشرية. يعد هذا أمرًا مناسبًا في الواقع إذا كان لدينا تسوق إلكتروني ونحتاج إلى إظهار $0.30
. في حالات أخرى، يمكننا استخدام علامة الجمع الأحادية لتحويلها إلى رقم:
دع المبلغ = 0.1 + 0.2؛ تنبيه( +sum.toFixed(2) ); // 0.3
يمكننا أيضًا مضاعفة الأرقام مؤقتًا في 100 (أو رقم أكبر) لتحويلها إلى أعداد صحيحة، وإجراء العمليات الحسابية، ثم القسمة مرة أخرى. بعد ذلك، بينما نجري العمليات الحسابية باستخدام الأعداد الصحيحة، يقل الخطأ إلى حدٍ ما، لكننا ما زلنا نحصل عليه عند القسمة:
تنبيه( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 تنبيه( (0.28 * 100 + 0.14 * 100) / 100)؛ // 0.420000000000001
لذا، فإن أسلوب الضرب/القسمة يقلل الخطأ، لكنه لا يزيله تمامًا.
في بعض الأحيان يمكننا أن نحاول تجنب الكسور على الإطلاق. مثلاً، إذا كنا نتعامل مع متجر، فيمكننا تخزين الأسعار بالسنت بدلاً من الدولار. ولكن ماذا لو طبقنا خصم 30%؟ من الناحية العملية، نادرًا ما يكون التهرب التام من الكسور أمرًا ممكنًا. فقط قم بتدويرها لقص "ذيول" عند الحاجة.
الشيء المضحك
حاول تشغيل هذا:
// مرحبًا! أنا رقم يتزايد بذاته! تنبيه(999999999999999 ); // يظهر 1000000000000000
وهذا يعاني من نفس المشكلة: فقدان الدقة. هناك 64 بت للرقم، 52 منها يمكن استخدامها لتخزين الأرقام، لكن هذا ليس كافيًا. لذلك تختفي الأرقام الأقل أهمية.
لا يؤدي جافا سكريبت إلى حدوث خطأ في مثل هذه الأحداث. إنها تبذل قصارى جهدها لتناسب الرقم بالتنسيق المطلوب، ولكن لسوء الحظ، هذا التنسيق ليس كبيرًا بما يكفي.
صفرين
نتيجة أخرى مضحكة للتمثيل الداخلي للأرقام هي وجود صفرين: 0
و -0
.
وذلك لأن الإشارة يتم تمثيلها ببتة واحدة، لذلك يمكن ضبطها أو عدم تعيينها لأي رقم بما في ذلك الصفر.
في معظم الحالات، يكون التمييز غير ملحوظ، لأن المشغلين مناسبون للتعامل معهم على أنهم نفس الشيء.
هل تتذكر هاتين القيمتين الرقميتين الخاصتين؟
Infinity
(و -Infinity
) هي قيمة رقمية خاصة أكبر (أقل) من أي شيء آخر.
NaN
يمثل خطأ.
وهي تنتمي إلى النوع type number
، ولكنها ليست أرقامًا "عادية"، لذا توجد وظائف خاصة للتحقق منها:
تقوم isNaN(value)
بتحويل وسيطتها إلى رقم ثم تختبرها لكونها NaN
:
تنبيه(isNaN(NaN)); // حقيقي تنبيه(isNaN("str")); // حقيقي
لكن هل نحتاج إلى هذه الوظيفة؟ ألا يمكننا فقط استخدام المقارنة === NaN
؟ للأسف لا. القيمة NaN
فريدة من نوعها من حيث أنها لا تساوي أي شيء، بما في ذلك نفسها:
تنبيه (NaN === NaN)؛ // خطأ شنيع
تقوم isFinite(value)
بتحويل الوسيط الخاص بها إلى رقم وإرجاع true
إذا كان رقمًا عاديًا، وليس NaN/Infinity/-Infinity
:
تنبيه(isFinite("15")); // حقيقي تنبيه(isFinite("str")); // خطأ، لأن القيمة الخاصة هي NaN تنبيه(isFinite(Infinity)); // خطأ، لأن القيمة الخاصة هي: اللانهاية
في بعض الأحيان يتم استخدام isFinite
للتحقق مما إذا كانت قيمة السلسلة عبارة عن رقم عادي:
Let num = +prompt("أدخل رقمًا"، ''); // سيكون صحيحًا إلا إذا قمت بإدخال Infinity أو -Infinity أو ليس رقمًا تنبيه(isFinite(num));
يرجى ملاحظة أن السلسلة الفارغة أو ذات المسافة فقط يتم التعامل معها على أنها 0
في جميع الدوال الرقمية بما في ذلك isFinite
.
Number.isNaN
و Number.isFinite
تعتبر أساليب Number.isNaN وNumber.isFinite هي الإصدارات الأكثر "صرامة" من وظائف isNaN
و isFinite
. إنهم لا يقومون بتحويل الوسيطة الخاصة بهم تلقائيًا إلى رقم، ولكنهم يتحققون مما إذا كانت تنتمي إلى نوع number
بدلاً من ذلك.
تُرجع Number.isNaN(value)
true
إذا كانت الوسيطة تنتمي إلى نوع number
وكانت NaN
. وفي أي حالة أخرى، فإنه يعود false
.
تنبيه( Number.isNaN(NaN)); // حقيقي تنبيه( Number.isNaN("str" / 2) ); // حقيقي // لاحظ الفرق: تنبيه( Number.isNaN("str")); // خطأ، لأن "str" ينتمي إلى نوع السلسلة، وليس إلى نوع الرقم تنبيه(isNaN("str")); // صحيح، لأن isNaN يحول السلسلة "str" إلى رقم ويحصل على NaN نتيجة لهذا التحويل
تُرجع Number.isFinite(value)
القيمة true
إذا كانت الوسيطة تنتمي إلى نوع number
ولم تكن NaN/Infinity/-Infinity
. وفي أي حالة أخرى، فإنه يعود false
.
تنبيه( Number.isFinite(123)); // حقيقي تنبيه( Number.isFinite(Infinity)); // خطأ شنيع تنبيه( Number.isFinite(2 / 0)); // خطأ شنيع // لاحظ الفرق: تنبيه( Number.isFinite("123") ); // خطأ، لأن "123" ينتمي إلى نوع السلسلة، وليس إلى نوع الرقم تنبيه(isFinite("123")); // صحيح، لأن isFinite يحول السلسلة "123" إلى رقم 123
بطريقة ما، تعد Number.isNaN
و Number.isFinite
أبسط وأكثر وضوحًا من الدالتين isNaN
و isFinite
. من الناحية العملية، يتم استخدام isNaN
و isFinite
في الغالب، حيث أنهما أقصر في الكتابة.
مقارنة مع Object.is
هناك طريقة مدمجة خاصة Object.is
تقارن قيمًا مثل ===
، ولكنها أكثر موثوقية في حالتي الحافة:
إنه يعمل مع NaN
: Object.is(NaN, NaN) === true
، هذا شيء جيد.
تختلف القيمتان 0
و -0
: Object.is(0, -0) === false
، وهذا صحيح من الناحية الفنية لأن الرقم يحتوي داخليًا على بت إشارة قد يكون مختلفًا حتى لو كانت جميع البتات الأخرى أصفارًا.
في جميع الحالات الأخرى، Object.is(a, b)
هو نفسه a === b
.
نذكر Object.is
هنا، لأنه غالبًا ما يستخدم في مواصفات JavaScript. عندما تحتاج خوارزمية داخلية إلى مقارنة قيمتين لتكونا متماثلتين تمامًا، فإنها تستخدم Object.is
(وتسمى داخليًا SameValue).
يعد التحويل الرقمي باستخدام علامة الجمع +
أو Number()
أمرًا صارمًا. إذا لم تكن القيمة رقمًا بالضبط، فإنها تفشل:
تنبيه(+"100px"); // نان
الاستثناء الوحيد هو المسافات في بداية السلسلة أو نهايتها، حيث يتم تجاهلها.
لكن في الحياة الواقعية، غالبًا ما تكون لدينا قيم بوحدات، مثل "100px"
أو "12pt"
في CSS. وفي العديد من البلدان أيضًا، يأتي رمز العملة بعد المبلغ، لذلك لدينا "19€"
ونرغب في استخراج قيمة رقمية منها.
هذا هو الغرض من parseInt
و parseFloat
.
إنهم "يقرأون" رقمًا من سلسلة حتى لا يتمكنوا من ذلك. في حالة وجود خطأ، يتم إرجاع الرقم المجمع. تقوم الدالة parseInt
بإرجاع عدد صحيح، بينما تقوم parseFloat
بإرجاع رقم الفاصلة العائمة:
تنبيه (parseInt('100px') ); // 100 تنبيه (parseFloat('12.5em') ); // 12.5 تنبيه (parseInt('12.3') ); // 12، يتم إرجاع الجزء الصحيح فقط تنبيه (parseFloat('12.3.4') ); // 12.3 النقطة الثانية توقف القراءة
هناك حالات عندما يقوم parseInt/parseFloat
بإرجاع NaN
. يحدث ذلك عندما لا يمكن قراءة أي أرقام:
تنبيه (parseInt('a123') ); // NaN، الرمز الأول يوقف العملية
الوسيطة الثانية لـ parseInt(str, radix)
تحتوي الدالة parseInt()
على معلمة ثانية اختيارية. فهو يحدد أساس النظام الرقمي، لذلك يمكن parseInt
أيضًا تحليل سلاسل من الأرقام السداسية والأرقام الثنائية وما إلى ذلك:
تنبيه (parseInt('0xff', 16) ); // 255 تنبيه (parseInt('ff', 16) ); // 255، بدون 0x يعمل أيضًا تنبيه (parseInt('2n9c', 36) ); // 123456
تحتوي JavaScript على كائن Math مدمج يحتوي على مكتبة صغيرة من الوظائف والثوابت الرياضية.
بعض الأمثلة:
Math.random()
إرجاع رقم عشوائي من 0 إلى 1 (لا يشمل 1).
تنبيه( Math.random()); // 0.1234567894322 تنبيه (Math.random ())؛ // 0.5435252343232 تنبيه( Math.random()); // ... (أي أرقام عشوائية)
Math.max(a, b, c...)
و Math.min(a, b, c...)
إرجاع الأكبر والأصغر من العدد التعسفي للوسائط.
تنبيه( Math.max(3, 5, -10, 0, 1) ); // 5 تنبيه( Math.min(1, 2)); // 1
Math.pow(n, power)
إرجاع n
مرفوع إلى القوة المعطاة.
تنبيه( Math.pow(2, 10)); // 2 في القوة 10 = 1024
هناك المزيد من الوظائف والثوابت في كائن Math
، بما في ذلك علم المثلثات، والتي يمكنك العثور عليها في المستندات الخاصة بكائن الرياضيات.
لكتابة أرقام تحتوي على العديد من الأصفار:
قم بإلحاق "e"
مع عدد الأصفار للرقم. مثل: 123e6
هو نفس 123
مع 6 أصفار 123000000
.
الرقم السالب بعد "e"
يؤدي إلى تقسيم الرقم على 1 مع وجود أصفار معينة. على سبيل المثال 123e-6
يعني 0.000123
( 123
جزءًا من مليون).
لأنظمة الأرقام المختلفة:
يمكنه كتابة الأرقام مباشرة بالأنظمة السداسية ( 0x
) والثمانية ( 0o
) والثنائية ( 0b
).
يقوم parseInt(str, base)
بتوزيع السلسلة str
إلى عدد صحيح في نظام الأرقام base
معينة، 2 ≤ base ≤ 36
.
num.toString(base)
يحول رقمًا إلى سلسلة في نظام الأرقام باستخدام base
المحددة.
لاختبارات الأرقام العادية:
تقوم isNaN(value)
بتحويل الوسيطة الخاصة بها إلى رقم ثم تختبرها للتأكد من كونها NaN
يتحقق Number.isNaN(value)
مما إذا كانت الوسيطة الخاصة به تنتمي إلى نوع number
، وإذا كان الأمر كذلك، فإنه يختبرها لكونها NaN
تقوم isFinite(value)
بتحويل وسيطتها إلى رقم ثم تختبرها لعدم كونها NaN/Infinity/-Infinity
يتحقق Number.isFinite(value)
مما إذا كانت الوسيطة الخاصة به تنتمي إلى نوع number
، وإذا كان الأمر كذلك، فإنه يختبرها للتأكد من أنها ليست NaN/Infinity/-Infinity
لتحويل قيم مثل 12pt
و 100px
إلى رقم:
استخدم parseInt/parseFloat
للتحويل "الناعم"، الذي يقرأ رقمًا من سلسلة ثم يُرجع القيمة التي كان بإمكانه قراءتها قبل الخطأ.
للكسور:
قم بالتقريب باستخدام Math.floor
أو Math.ceil
أو Math.trunc
أو Math.round
أو num.toFixed(precision)
.
تأكد من تذكر أن هناك فقدانًا للدقة عند التعامل مع الكسور.
المزيد من الوظائف الرياضية:
شاهد كائن Math عندما تحتاج إليه. المكتبة صغيرة جدًا ولكنها يمكنها تغطية الاحتياجات الأساسية.
الأهمية: 5
قم بإنشاء برنامج نصي يطالب الزائر بإدخال رقمين ثم يعرض مجموعهما.
قم بتشغيل العرض التوضيحي
ملاحظة: هناك مسكتك مع الأنواع.
Let a = +prompt("الرقم الأول؟", ""); Let b = +prompt("الرقم الثاني؟", ""); تنبيه (أ + ب)؛
لاحظ علامة الجمع الأحادية +
قبل prompt
. يقوم على الفور بتحويل القيمة إلى رقم.
بخلاف ذلك، فإن a
و b
سيكونان سلسلة، وسيكون مجموعهما هو التسلسل، أي: "1" + "2" = "12"
.
الأهمية: 4
وفقًا للوثائق، يقوم كل من Math.round
و toFixed
بالتقريب إلى أقرب رقم: 0..4
يؤدي إلى الأسفل بينما 5..9
يؤدي إلى الأعلى.
على سبيل المثال:
تنبيه( 1.35.toFixed(1) ); // 1.4
في المثال المشابه أدناه، لماذا تم تقريب 6.35
إلى 6.3
وليس 6.4
؟
تنبيه( 6.35.toFixed(1) ); // 6.3
كيفية تقريب 6.35
بالطريقة الصحيحة؟
داخليًا، الكسر العشري 6.35
هو ثنائي لا نهاية له. وكما هو الحال دائمًا في مثل هذه الحالات، يتم تخزينه بفقدان الدقة.
دعونا نرى:
تنبيه( 6.35.toFixed(20) ); // 6.349999999999964473
يمكن أن يؤدي فقدان الدقة إلى زيادة أو نقصان الرقم. في هذه الحالة بالذات، يصبح الرقم أقل قليلاً، ولهذا السبب تم تقريبه إلى الرقم الأدنى.
وما هو 1.35
؟
تنبيه( 1.35.toFixed(20) ); // 1.3500000000000008882
وهنا أدى فقدان الدقة إلى جعل الرقم أكبر قليلاً، لذلك تم تقريبه.
كيف يمكننا حل مشكلة العدد 6.35
إذا أردنا تقريبه بالطريقة الصحيحة؟
يجب أن نقربه من عدد صحيح قبل التقريب:
تنبيه ((6.35 * 10).toFixed(20) ); // 63.5000000000000000000
لاحظ أن 63.5
لا يحتوي على فقدان الدقة على الإطلاق. ذلك لأن الجزء العشري 0.5
هو في الواقع 1/2
. الكسور المقسومة على قوى العدد 2
ممثلة تمامًا في النظام الثنائي، والآن يمكننا تقريبها:
تنبيه( Math.round(6.35 * 10) / 10 ); // 6.35 -> 63.5 -> 64 (مقربًا) -> 6.4
الأهمية: 5
قم بإنشاء وظيفة readNumber
التي تطالب برقم حتى يقوم الزائر بإدخال قيمة رقمية صالحة.
يجب إرجاع القيمة الناتجة كرقم.
كما يمكن للزائر إيقاف العملية عن طريق إدخال سطر فارغ أو الضغط على "إلغاء". في هذه الحالة، يجب أن ترجع الدالة null
.
قم بتشغيل العرض التوضيحي
افتح صندوق الرمل مع الاختبارات.
وظيفة قراءة رقم () { اسمحوا الأسطوانات؛ يفعل { num = موجه("أدخل رقمًا من فضلك؟", 0); } بينما ( !isFinite(num) ); if (num === null || num === '') return null; إرجاع +رقم؛ } تنبيه("قراءة: ${readNumber()}`);
الحل أكثر تعقيدًا بعض الشيء لأنه قد يكون لأننا بحاجة إلى التعامل مع الأسطر null
/الفارغة.
لذلك نحن في الواقع نقبل الإدخال حتى يصبح "رقمًا عاديًا". كلا من null
(إلغاء) والسطر الفارغ يتناسبان أيضًا مع هذا الشرط، لأنهما في شكل رقمي هما 0
.
بعد أن توقفنا، نحتاج إلى التعامل مع السطر null
والخالي بشكل خاص (إرجاع null
)، لأن تحويلهما إلى رقم سيرجع 0
.
افتح الحل بالاختبارات في وضع الحماية.
الأهمية: 4
هذه الحلقة لا نهائية. لا ينتهي أبدا. لماذا؟
دعني = 0؛ بينما (ط! = 10) { أنا += 0.2؛ }
هذا لأنني i
يساوي 10
أبدًا.
قم بتشغيله لمعرفة القيم الحقيقية لـ i
:
دعني = 0؛ بينما (ط < 11) { أنا += 0.2؛ إذا (i> 9.8 && i <10.2) تنبيه(i); }
لا أحد منهم هو بالضبط 10
.
تحدث مثل هذه الأشياء بسبب خسائر الدقة عند إضافة كسور مثل 0.2
.
الخلاصة: التهرب من التحقق من المساواة عند التعامل مع الكسور العشرية.
الأهمية: 2
تقوم الدالة Math.random()
المضمنة بإنشاء قيمة عشوائية من 0
إلى 1
(لا تتضمن 1
).
اكتب الدالة random(min, max)
لإنشاء رقم فاصلة عائمة عشوائي من min
إلى max
(لا يشمل max
).
أمثلة على عملها:
تنبيه(عشوائي(1, 5)); // 1.2345623452 تنبيه(عشوائي(1, 5)); // 3.7894332423 تنبيه(عشوائي(1, 5)); // 4.3435234525
نحن بحاجة إلى "تعيين" جميع القيم من الفاصل الزمني 0...1 إلى قيم من min
إلى max
.
ويمكن القيام بذلك على مرحلتين:
إذا ضربنا رقمًا عشوائيًا من 0…1 في max-min
، فإن الفاصل الزمني للقيم المحتملة يزيد من 0..1
إلى 0..max-min
.
الآن إذا أضفنا min
، يصبح الفاصل الزمني المحتمل من min
إلى max
.
الوظيفة:
وظيفة عشوائية (دقيقة، ماكس) { return min + Math.random() * (max - min); } تنبيه(عشوائي(1, 5)); تنبيه(عشوائي(1, 5)); تنبيه(عشوائي(1, 5));
الأهمية: 2
قم بإنشاء دالة randomInteger(min, max)
التي تنشئ رقمًا صحيحًا عشوائيًا من min
إلى max
بما في ذلك كل من min
و max
كقيم محتملة.
أي رقم من الفاصل الزمني min..max
يجب أن يظهر بنفس الاحتمال.
أمثلة على عملها:
تنبيه(randomInteger(1, 5)); // 1 تنبيه(randomInteger(1, 5)); // 3 تنبيه(randomInteger(1, 5)); // 5
يمكنك استخدام حل المهمة السابقة كأساس.
الحل الأبسط ولكن الخاطئ هو توليد قيمة من min
إلى max
وتقريبها:
دالة عدد صحيح عشوائي (دقيقة، كحد أقصى) { Let rand = min + Math.random() * (max - min); إرجاع Math.round(rand); } تنبيه(randomInteger(1, 3));
الدالة تعمل، ولكنها غير صحيحة. احتمال الحصول على قيم min
والحد max
أقل مرتين من أي احتمال آخر.
إذا قمت بتشغيل المثال أعلاه عدة مرات، فمن السهل أن ترى أن 2
يظهر في أغلب الأحيان.
يحدث ذلك لأن Math.round()
تحصل على أرقام عشوائية من الفاصل الزمني 1..3
وتقوم بتقريبها كما يلي:
القيم من 1... إلى 1.4999999999 تصبح 1 القيم من 1.5... إلى 2.4999999999 تصبح 2 القيم من 2.5... إلى 2.9999999999 تصبح 3
الآن يمكننا أن نرى بوضوح أن 1
يحصل على قيم أقل مرتين من 2
. ونفس الشيء مع 3
.
هناك العديد من الحلول الصحيحة للمهمة. واحد منهم هو ضبط حدود الفاصل الزمني. لضمان نفس الفترات، يمكننا توليد قيم من 0.5 to 3.5
، وبالتالي إضافة الاحتمالات المطلوبة إلى الحواف:
دالة عدد صحيح عشوائي (دقيقة، كحد أقصى) { // الآن الراند من (min-0.5) إلى (max+0.5) Let rand = min - 0.5 + Math.random() * (max - min + 1); إرجاع Math.round(rand); } تنبيه(randomInteger(1, 3));
هناك طريقة بديلة تتمثل في استخدام Math.floor
لرقم عشوائي من min
إلى max+1
:
دالة عدد صحيح عشوائي (دقيقة، كحد أقصى) { // هنا الراند من الحد الأدنى إلى (الحد الأقصى+1) Let rand = min + Math.random() * (max + 1 - min); إرجاع Math.floor(rand); } تنبيه(randomInteger(1, 3));
الآن يتم تعيين كافة الفواصل الزمنية بهذه الطريقة:
القيم من 1... إلى 1.9999999999 تصبح 1 القيم من 2... إلى 2.9999999999 تصبح 2 القيم من 3... إلى 3.9999999999 تصبح 3
جميع الفواصل الزمنية لها نفس الطول، مما يجعل التوزيع النهائي موحدًا.