مقدمه

در خیلی از زبان‌های برنامه‌نویسی، چندین نوع داده برای مقادیر عددی تعریف شده است؛ چند نوع برای اعداد صحیح و چند نوع برای اعداد اعشاری. حتی در زبان دینامیک و سطح بالایی مثل پایتون هم برای اعداد صحیح و اعشاری (و مختلط) نوع‌های مجزایی در نظر گرفته شده است. اما جاوااسکریپت فقط یک نوع number را برای مقادیر عددی، چه صحیح و چه اعشاری، در نظر گرفته است (و البته نوع bigint که کاربری متفاوتی دارد). نوع number از روی استانداردی به نام IEEE-754 پیاده‌سازی شده و معادل double یا f64 در زبان‌هایی مانند C# و Rust است. به علاوه، در درس گذشته گفتیم که نوع number علاوه بر اعداد، شامل مقادیر خاص Infinity و NaN هم هست که مفاهیم بی‌نهایت و مبهم در ریاضیات را (البته با کمی تفاوت) تداعی می‌کنند. در اینجا جزئیات بیشتری از این مقادیر خواهیم دید.

نمایش اعداد در جاوااسکریپت

جاوااسکریپت اعداد را به همان شکلی نمایش می‌دهد که ما انتظار داریم؛ یعنی در مبنای ده. اما امکان نمایش اعداد در مبناهای دیگر را هم دارد و تسهیلاتی را نیز برای کار با مقادیر عددی تدارک دیده است. با یک نمونه از همین تسهیلات شروع می‌کنیم: استفاده از کاراکتر _ به‌عنوان جداکننده‌ی ارقام.

فرض کنید بخواهیم عدد یک‌میلیارد را در یک متغیر جاوااسکریپت ذخیره کنیم. این کار را می‌توانیم به صورت زیر انجام دهیم.

Copy Icon numbers.js
let billion = 1000000000;

اما خواندن این عدد و شمارش تعداد صفرهای آن کمی مشکل به نظر می‌رسد. جاوااسکریپت کاراکتر _ در بین اعداد را نادیده می‌گیرد و ما می‌توانیم از این کاراکتر به عنوان جدا کننده‌ی ارقام استفاده کنیم تا خواندن اعداد ساده‌تر شود. پس می‌توانیم گزاره‌ی بالا را به صورت زیر بنویسیم.

Copy Icon numbers.js
let billion = 1_000_000_000;
console.log(billion);  //1000000000

علاوه بر این، در جاوااسکریپت از نمادگذاری علمی (scientific notation) نیز برای نمایش اعداد پشتیبانی می‌شود.

Copy Icon numbers.js
let billion = 1e9;    // 1 × 109
console.log(billion); // 1000000000
let mcs = 1e-6;       // 1 x 10-6
console.log(mcs);     // 0.000001
let x = 1.23e-6;      // 1.23 x 10-6
console.log(x);       // 0.0000123 

همانطور که گفتیم، امکان نمایش اعداد در مبناهایی غیر از 10 هم وجود دارد. برای نمایش اعداد در مبنای 16 (هگزادسیمال) کافیست پیشوند 0x را قبل از عدد قرار دهیم.

Copy Icon numbers.js
let x = 0xff;       // => 255: 15 x 160 + 15 x 161
let y = 0xBADCAFE;  // => 195939070
let z = 0x200;      // => 512

علاوه بر اعداد هگزادسیمال، اعداد باینری (در مبنای 2) و اعداد اکتال (در مبنای 8) هم در جاوااسکریپت پشتیبانی می‌شوند. اعداد باینری با پیشوند 0b و اعداد اکتال با 0o شروع می‌شوند.

Copy Icon numbers.js
let x = 0b10101;  // => 21: 1 x 20 + 0 x 21 + 1 x 22 + 0 x 23 + 1 x 24
let y = 0b1010_0001;  
            
let z = 0o377;    // => 255: 7 x 80 + 7 x 81 + 3 x 82

نوع number دارای متدی به نام toString() است که اعداد را به رشته (string) تبدیل می‌کند. اما اگر یک عدد را به عنوان آرگومان به این متد بدهیم، آن را به عنوان مبنا در نظر می‌گیرد و عدد مورد نظر را به آن مبنا تبدیل کرده و به صورت یک رشته برمی‌گرداند. پس، متد num.toString(base) عدد num را به مبنای base برده و آن را به رشته تبدیل می‌کند. این متد می‌تواند هر عدد بین 2 تا 36 را به عنوان آرگومان دریافت کند.

Copy Icon numbers.js
let n = 255;

console.log(n.toString(16));  // ff
console.log(n.toString(2));   // 11111111
console.log(n.toString(36))     // 73

خطاهای مربوط به محاسبات عددی

خطا در محاسبات عددی و هنگام کار با مقادیر عددی در زبان‌های برنامه‌نویسی، امری اجتناب‌ناپذیر است. این امر یک دلیل ساده دارد: در جاوااسکریپت، نوع number دارای یک فرمت 64 بیتی است و می‌تواند تعداد مشخص و متناهی عدد را نمایش دهد. وجود خطاهای محاسباتی در جاوااسکریپت و سایر زبان‌ها آنقدر که شاید در ابتدا به نظر برسد، مهم نیست. با این وجود، ما باید از وجود این خطاها و کم و کیف رخ دادنشان آگاه باشیم. خطاهای محاسباتی را برای اعداد صحیح و اعشاری به صورت جداگانه بررسی می‌کنیم.

خطای دقت برای اعداد صحیح

قبلاً هم گفتیم که number یک نوع 64 بیتی است و بنابراین، نمی‌تواند یک عدد صحیح خیلی بزرگ را نمایش دهد. برای اعداد صحیح یک بازه‌ی امن وجو دارد که ±(253 - 1) است. اعداد صحیح درون این بازه به صورت دقیق و بدون خطا نمایش داده می‌شوند. اما خارج از این بازه ما با خطایی به نام خطای دقت (precision error) مواجه هستیم که مثال زیر آن را نشان می‌دهد.

> 2 ** 53 == 2 ** 53 + 1
true
> 2 ** 54 == 2** 54 + 2
true
> 1e20 == 1e20 + 1000 
true
> 1e308 == 1e308 + 1e200
true
> 1e309
Infinity
> -1e500
-Infinity
> 1e-500
0
          

همانطور که می‌بینید، با بزرگ شدن عدد، دقت نمایش آن کم می‌شود و از جایی به بعد، معادل Infinity در نظر گرفته می‌شود. عبارت آخر هم نشان می‌دهد که اگر نتیجه‌ی یک محاسبه‌ی عددی مقداری باشد که نسبت به کوچکترین عدد قابل نمایش، به صفر نزدیکتر باشد، مقدار صفر برگردانده می‌شود.

با این حال، همانطور که قبلاً هم گفتیم در معدود سناریوهایی که به اعداد صحیح خارج از بازه‌ی امن بدون تحمل خطای دقت نیاز داریم، از نوع bigint استفاده می‌کنیم.

خطای تقریب برای اعداد اعشاری

پیاده‌سازی استاندارد IEEE-754 که اعداد را در فرمت باینری در حافظه‌ی کامپیوتر نمایش می‌دهد، باعث می‌شود که گاهی به موارد عجیبی مثل نمونه‌ی زیر بربخوریم.

> (0.1 + 0.2) == 0.3 
false
> 0.1 + 0.2 
0.30000000000000004
          

عبارت اول نشان می‌دهد که حاصلجمع 0.1 و 0.2 با 0.3 برابر نیست و نتیجه‌ی عبارت دوم نشان می‌دهد که حاصل این جمع برابر با چه مقداری است. این امر شاید در ابتدا عجیب به نظر برسد اما دلیل واضحی دارد.

همانطور که برای ما که از سیستم دسیمال یا دهدهی استفاده می‌کنیم، امکان نمایش دقیق کسرهایی مثل 1 / 3 وجود ندارد و این تقسیم به یک عدد بی‌پایان 0.333333… منجر می‌شود، در سیستم باینری یا دودویی هم نمی‌توان اعدادی مثل 0.1 را به‌صورت دقیق نمایش داد.

دقت داشته باشید که این محدودیت مختص جاوااسکریپت نیست و در هر زبانی وجود دارد. اما طبیعتاً باید راهی برای کنترل این وضعیت وجود داشته باشد. اولین چیزی که در این خصوص مهم است، آگاهی از آن است. یعنی باید بدانیم که چنین محدودیتی وجود دارد و به‌خصوص در مقایسه‌ها این موضوع را مد نظر قرار دهیم تا با نتایح غیرمنتظره مواجه نشویم. اما علاوه بر این، بهترین راهی که برای کنترل این مشکل داریم این است که از متد num.toFixed(n) استفاده کنیم که عدد num را تا n رقم اعشار گرد می‌کند.

> let sum = 0.1 + 0.2;
> sum.toFixed(2)
"0.30"
          

دقت داشته باشید که خروجی متد toFixed() یک رشته است و بنابراین، اگر می‌خواهیم از این خروجی به عنوان عدد استفاده کنیم، باید آن را با استفاده از تابع Number() یا عملگر + به عدد تبدیل کنیم.

> let sum = 0.2 + 0.1;
> +sum.toFixed(2) * 4
1.2
          

نوع number و شیء Number

یادآوری می‌کنم که در جاوااسکریپت، هر مقدار یا متعلق به یک نوع primitive است و یا یک object است. از طرفی، همانطور که در فصل اول گفتیم، جاوااسکریپت از پارادایم شی‌گرایی پشتیبانی می‌کند و بنابراین، در این زبان هر چیزی به نوبه‌ی خود یک object یا شیء است. ظاهراً ما با یک تناقض مواجه هستیم: مقادیر primitive هم شیء هستند و هم نیستند. البته واقعاً تناقضی وجود ندارد و این ابهام از تفاوت ترمینولوژی در زبان‌های مختلف ناشی می‌شود و کمی جلوتر آن را رفع می‌کنیم. اما سازنده‌ی زبان جاوااسکریپت با یک تناقض واقعی مواجه بوده است.

یک شیء یا object دارای تعدادی پراپرتی است که این پراپرتی‌ها می‌توانند از نوع داده‌ای یا متد باشند. طبیعتاً نوع‌ها و مقادیر primitive هم می‌توانند از این مزیت برخوردار باشند و پراپرتی‌هایی داشته باشند اما در این صورت، یک مزیت دیگر آنها، یعنی سریع‌تر بودن نسبت به نوع object از بین می‌رود. مسئله‌ای که سازنده‌ی زبان با آن مواجه بوده این است که: چطور می‌توان ترتیبی اتخاذ کرد که نوع‌های primitive هم سریع باشند و هم بتوانند دارای پراپرتی باشند.

راه حلی که برای این مسئله انتخاب شده، بسیار هوشمندانه است: یک object wrapper که به صورت دینامیک و در زمان نیاز ساخته شده و سپس تخریب می‌شود. به این ترتیب، برای هر نوع primitive یک object wrapper تعریف شده که شامل پراپرتی‌های مورد نیاز آن نوع است اما این object wrapper فقط وقتی ایجاد می‌شود که به آن نیاز داریم و پس از استفاده (یعنی بعد از دسترسی به پراپرتی مورد نظر) تخریب می‌شود. به این ترتیب، هر دو خواسته‌ی سازنده برآورده می‌شود؛ یعنی نوع‌های primitive همچنان سریع هستند و به پراپرتی‌هایی هم دسترسی دارند.

با این حساب، باید تفاوت بین نوع number و object wrapper یا (با کمی اغماض) شیء Number برایتان روشن شده باشد. همین‌طور تفاوت بین نوع string و شیء String یا تفاوت بین نوع boolean و شیء Boolean. در واقع، به همین دلیل است که وقتی از عملگر typeof مثلاً برای یک مقدار عددی استفاده می‌کنیم، مقدار number را برمی‌گرداند اما وقتی (همانطور که در ادامه خواهیم دید) قصد داریم از یک پراپرتی برای یک مقدار عددی استفاده کنیم، آن را به صورت Number.property فراخوانی می‌کنیم.

بنابراین، می‌توان گفت در جاوااسکریپت هر مقداری حتی اگر از یک نوع object نباشد، یک شیء محسوب می‌شود و می‌تواند به پراپرتی‌های تعریف شده روی شیء خود دسترسی داشته باشد. تنها موارد استثنا برای این امر مقادیر null و undefined هستند که به هیچ پراپرتی دسترسی ندارند.

مهمترین پراپرتی‌های شیء Number

با توجه به مطالب گفته شده، می‌دانیم که مقادیر عددی با وجود اینکه از یک نوع primitive هستند، به پراپرتی‌های تعریف شده روی شیء Number دسترسی دارند. مهمترین این پراپرتی‌ها به همراه توضیحی در مورد کاربردشان در جدول زیر لیست شده‌اند.

نام پراپرتی فرم پراپرتی توضیح
ثابت‌های بی‌نهایت و اپسیلون Number.EPSILON
Number.POSITIVE_INFINITY
Number.NEGATIVE_INFINITY
Infinity
-Infinity
ثابت EPSILON برابر با اختلاف بین 1 و کوچکترین عدد بزرگتر از 1 است. ثابت POSITIVE_INFINITY و معادل global آن یعنی Infinity مقداری است که از بزرگترین مقدار عددی قابل نمایش در جاوااسکریپت بزرگتر است. به همین ترتیب، NEGATIVE_INFINITY و معادل global آن یعنی -Infinity نیز مقداری است کوچکتر از بزرگترین عدد منفی قابل نمایش.
ثابت‌های بیشترین و کمترین مقدار عددی Number.MIN_VALUE
Number.MAX_VALUE
Number.MIN_SAFE_INTEGER
Number.MAX_SAFE_INTEGER
ثابت MIN_VALUE نزدیکترین عدد به صفر در بین اعداد قابل نمایش در جاوااسکریپت است و تقریباً‌ برابر با 5×10-324 است. MAX_VALUE نیز بزرگترین عدد قابل نمایش در جاوااسکریپت است که تقریباً‌ برابر با 1.79×10308 است. دو ثابت بعدی این گروه نیز به ترتیب کوچکترین و بزرگترین عدد صحیحی هستند که نوع Number می‌تواند به طور دقیق نمایش دهد. این اعداد برابر با -(253-1) و 253-1 هستند.
مقدار غیرعددی Number.NaN
NaN
Number.isNaN(x)
NaN بیانگر یک خطای محاسباتی در جاوااسکریپت است. همانطور که می‌بینید، این مقدار به عنوان یک مقدار global نیز در دسترس است. تابع isNaN() نیز آرگومانش را از نظر برابری با NaN بررسی می‌کند و در صورت برابری مقدار true را برمی‌گرداند.
توابع تبدیل اعداد Number.parseInt(s)
Number.parseFloat(s)
این دو تابع نیز برای تبدیل مقادیر رشته‌ای به اعداد صحیح و اعشاری کاربرد دارند.
توابع بررسی اعداد Number.isFinite(x)
isFinite(x)
Number.isInteger(x)
Number.isSafeInteger(x)
تابع isFinite() در صورتی که آرگومانش یک عدد متناهی باشد، مقدار true و در غیر این صورت، false را برمی‌گرداند. تابع isInteger() در صورتی که آرگومانش یک عدد صحیح باشد، مقدار true را برمی‌گرداند و تابع isSafeInteger() نیز در صورتی مقدار true را برمی‌گرداند که آرگومانش در محدوده‌ی بین MIN_SAFE_INTEGER و MAX_SAFE_INTEGER قرار داشته باشد.

بعضی از پراپرتی‌های جدول بالا به توضیحات و بیان جزئیات بیشتری نیاز دارند. این کار را در ادامه انجام می‌دهیم.

مقادیر Infinity و NaN

یک بار دیگر یادآوری می‌کنم که نوع number در جاوااسکریپت علاوه بر اعداد، شامل دو مقدار خاص دیگر هم هست:

  • مقدار Infinity-Infinity) که از هر عدد دیگر بزرگتر (کوچکتر) است.
  • مقدار NaN که بیانگر یک خطای محاسباتی است.

قبلاً دیدیم که علاوه بر اعداد خیلی بزرگ، تقسیم اعداد بر صفر نیز منجر به تولید مقدار Infinity می‌شود. هر عمل محاسباتی که بیانگر نوعی ابهام یا خطا باشد نیز منجر به تولید مقدار NaN می‌شود. عبارات زیر را در محیط کنسول مرورگر یا Node وارد کنید. تکرار می‌کنم که خط‌های بولد شده که با کاراکتر > شروع می‌شوند، گزاره یا عبارتی است که وارد می‌کنیم و سایر خطوط نتیجه‌ی تولید شده را نمایش می‌دهند.

> 1e500
Infinity 
> -1e500
-Infinity
> 1 / 0 
Infinity
> -1 / 0 
-Infinity
> 0 / 0
NaN
          

در ارتباط با مقادیر Infinity و NaN دو متد مهم داریم که زیاد به کار می‌آیند:

  • متد isNaN(value) که بررسی می‌کند آیا نتیجه‌ی تبدیل value به عدد برابر با NaN خواهد بود یا خیر. اگر پاسخ مثبت باشد، مقدار true و در غیر این صورت مقدار false را برمی‌گرداند. پس دقت داشته باشید که این متد، یکراست سراغ بررسی NaN بودن آرگومان خود نمی‌رود؛ بلکه ابتدا آن را بر اساس قوانین تبدیل به اعداد که در بالا بیان کردیم، به عدد تبدیل کرده و سپس مقایسه با NaN را انجام می‌دهد.
  • متد isFinite(value) هم ابتدا value را به عدد تبدیل می‌کند و سپس در صورتی که حاصل این تبدیل، یک عدد نرمال (یعنی یک مقدار عددی به‌جز Infinity، -Infinity و NaN) باشد، مقدار true و در غیر این صورت مقدار false را برمی‌گرداند.

مثال زیر کاربرد متدهای isNaN() و isFinite() را نشان می‌دهد.

> isNaN(NaN)
true
> isNaN(0 / 0) 
true
> isNaN(1/0) 
false
> isNaN("Hi")
true
> isNaN("1000")
false
> isNaN(false / 0)
true
> isFinite("15")
true
> isFinite("Hi")
false 
> isFinite(1 / 0)
false
          

isNaN() و isFinite() متدهای global هستند؛ یعنی همانطور که در مثال‌های بالا هم دیدیم، مستقیماً و بدون نیاز به هیچ شیئی آنها را فراخوانی می‌کنیم. اما یک ورژن سختگیرانه هم از این دو متد داریم که متعلق به نوع number هستند و باید به فرم Number.isFinite(value) و Number.isNaN(value) مورد استفاده قرار بگیرند.

با وجود شباهت زیادی که بین این متدها و همتاهای global آنها وجود دارد، یک تفاوت اساسی این متدها را از هم متمایز می‌کند. بر خلاف دو متد global که ابتدا تبدیل به عدد و سپس مقایسه را انجام می‌دهند، متدهای Number.isNaN(value) و Number.isFinite(value) تبدیلی انجام نمی‌دهند؛ بلکه اول بررسی می‌کنند که آیا value متعلق به نوع number هست یا نه و در صورت مثبت بودن جواب، مقایسه را با NaN و Infinity انجام می‌دهند. مثال زیر را ببینید.

> isNaN("Hi") 
true 
> Number.isNaN("Hi") 
false 
> isFinite("123") 
true
> Number.isFinite("123")
false
          

متدهای parseInt و parseFloat

تبدیل به مقادیر عددی با استفاده از تابع Number() یا عملگر + یک ماهیت سختگیرانه دارد. اگر بخواهیم مقداری مثل 100px را به عدد تبدیل کنیم، تابع Number("100px") مقدار NaN را برمی‌گرداند. گاهی اوقات این همان چیزی است که می‌خواهیم و بنابراین در این مواقع از همین روش برای تبدیل استفاده می‌کنیم. اما مواقعی هم هست که می‌خواهیم چنین تبدیلی با برگرداندن عدد 100 همراه باشد و این کاری است که بر عهده‌ی متدهای parseInt() و parseFloat() قرار دارد.

متدهای parseInt() و parseFloat() یک عدد را از یک رشته می‌خوانند تا جایی که دیگر امکان این کار وجود نداشته باشد. در نهایت متد parseInt() یک عدد صحیح و متذ parseFloat() یک عدد اعشاری برمی‌گردانند.

> parseInt("100px")
100
> parseFloat("12.5em")
12.5
$ parseInt("12.3rem") 
12 
> parseFloat("12.3.4") 
12.3
> parseInt("a123") 
NaN
          

عبارت آخر در مثال بالا نشان می‌هد که این متدها در صورتی که امکان شروع به خواندن عدد را نداشته باشند، مقدار NaN را برمی‌گردانند.

متد parseInt() می‌تواند یک آرگومان دوم هم دریافت کند و به فرم parseInt(str, base) به کار رود که در این صورت، str را در مبنای base در نظر گرفته و کار تبدیل را انجام می‌دهد. مثال زیر این مطلب را روشن می‌کند.

> parseInt("ff", 16)
255
> parseInt("2n9c", 36)
123456
          

شیء Math در جاوااسکریپت

در جاوااسکریپت، شیء Math تعدادی تابع و ثابت ریاضی ارائه می‌دهد که برای انجام محاسبات ریاضی پیچیده و رایج به کار می‌روند. تعدادی از مهمترین و پرکاربردترین توابع و ثابت‌های شیء Math را در جدول زیر آورده‌ایم و توضیح کوتاهی راجع به هر یک داده‌ایم.

پراپرتی شیء Math کاربرد
Math.pow()
Math.sqrt()
Math.cbrt()
این سه متد برای انجام اعمال توان و جذر کاربرد دارند. متد pow دو آرگومان دریافت کرده و اولی را به توان دومی می‌رساند. متد sqrt جذر یا ریشه‌ی دوم آرگومان خود و متد cbrt ریشه‌ی سوم آرگومان خود را برمی‌گرداند.
Math.max()
Math.min()
این دو متد همانطور که از نامشان پیداست، به‌ترتیب، بزرگترین و کوچکترین عدد را از بین چند عدد که به عنوان آرگومان دریافت کرده‌اند، برمی‌گردانند.
Math.sin()
Math.cos()
Math.tan()
Math.sinh()
Math.cosh()
Math.tanh()
سه متد اول توابع مثلثاتی و سه متد بعدی توابع مثلثاتی هذلولی یا Hyperbolic هستند. آرگومان این متدها بر حسب رادیان در نظر گرفته می‌شود.
Math.log()
Math.log10()
Math.exp()
اینها متدهایی هستند که توابع لگاریتم و نمایی را فراهم می‌کنند. متد log لگاریتم طبیعی (در مبنای e) است، متد log10 لگاریتم در مبنای 10 و متد exp عدد نپر یا e را به توان آرگومان خود می‌رساند.
Math.abs()
Math.sign()
تابع abs قدرمطلق آرگومانش را برمی‌گرداند و sign متدی است که تابع علامت را پیاده‌سازی می‌کند. تابع علامت در ریاضیات، برای ورودی‌های منفی مقدار -1 و برای ورودی‌های مثبت مقدار 1 و برای صفر مقدار صفر را برمی‌گرداند.
Math.random() از این متد برای ساخت اعداد تصادفی بین 0 و 1 استفاده می‌شود.
Math.floor()
Math.ceil()
Math.round()
Math.trunc()
این متدها برای گِرد کردن اعداد کاربرد دارند. متد floor آرگومانش را رو به پایین گرد می‌کند، متد ceil رو به بالا گرد می‌کند، متد round به نزدیکترین عدد صحیح گرد می‌کند و متد trunc بخش اعشار آرگومانش را حذف می‌کند.

شیء Math یک شیء استاتیک است؛ یعنی همه‌ی پراپرتی‌های آن به‌صورت Math.property در دسترس هستند. اما مثلاً در مورد نوع number دیدیم که برخی پراپرتی‌ها به صورت Number.property و برخی دیگر به صورت num.property در دسترس هستند که در آن num یک نمونه (instance) یا مقدار عددی است. پس، پراپرتی‌های یک نوع یا استاتیک هستند و یا نمونه‌ای و نوعی مثل Math که همه‌ی پراپرتی‌هایش استاتیک است، یک نوع استاتیک نامیده می‌شود.

اما حالا اجازه دهید با تست یک مثال در محیط کنسول، پراپرتی‌های جدول بالا را در عمل ببینیم.

> Math.pow(2, 5)
32
> Math.pow(27, 1/3)
3
> Math.cbrt(27)
3
> Math.max(8, 5, 11, 4, 9)
11
> Math.min(8, 5, 11, 4, 9)
4
> Math.sin(Math.PI / 2) 
1
> Math.cos(Math.PI) 
-1 
> log(Math.E ** 2) 
2
> log10(100)
2
> Math.abs(-2.5) 
2.5 
> Math.sign(-5) 
-1 
> Math.sign(4) 
1
> Math.random()
0.33756111148907686 
> Math.random() 
0.8495284999189805
> Math.floor(2.85)
2
> Math.ceil(2.25)
3
> Math.round(2.45) 
2
> Math.round(2.55)
3
> Math.round(2.5)
3
> Math.trunc(2.98)
2
          

نوع bigint

در ES 2020 یک نوع جدید با نام bigint برای کار با مقادیر عددی معرفی شد. خوشبختانه این ویژگی در اکثر مرورگرهای اصلی و همینطور Node.js پیاده‌سازی شده است. bigint همانطور که از نامش پیداست، یک نوع عددی است که مقادیرش اعداد صحیح هستند اما مقادیر bigint می‌توانند هزاران یا میلیون‌ها رقم داشته باشند.

لیترال‌های bigint به صورت دنباله‌ای از ارقام و یک حرف n در انتهای دنباله ایجاد می‌شوند. این لیترال‌ها به طور پیش‌فرض در مبنای 10 هستند و ما می‌توانیم همانند نوع number این لیترال‌ها را در مبناهای دیگر نیز نمایش دهیم.

برای تبدیل اعداد معمولی یا رشته‌ها به مقادیر bigint می‌توانیم از تابع BigInt() به عنوان یک تابع تبدیل استفاده کنیم.

Copy Icon JAVASCRIPT
let x = BigInt(Number.MAX_SAFE_INTEGER); 
let s = "1" + "0".repeat(100); 
let y = BigInt(s); // => 10n**100n: one googol
console.log(x);
console.log(y);

محاسبات روی مقادیر bigint به جز یک مورد کاملاً همانند اعداد عادی جاوااسکریپت است. این مورد استثنا در مورد عمل تقسیم رخ می‌دهد. حاصل تقسیم مقادیر bigint خارج‌قسمت تقسیم است و باقیمانده نادیده گرفته می‌شود.

> 1000n + 2000n // => 3000n
> 3000n - 2000n // => 1000n
> 2000n * 3000n // => 6000000n
> 3000n / 997n // => 3n
> 3000n % 997n // => 9n
          

همانطور که دیدیم، نمادهای عملگرهای حسابی با مقادیر bigint هم کار می‌کنند. اما توجه داشته باشید که امکان استفاده‌ی ترکیبی از این دو نوع مقادیر وجود ندارد. یعنی نمی‌توانیم یک عملگر حسابی را روی یک عملوند bigint و یک عملوند number به کار ببریم و عملوندها باید از یک نوع باشند. این موضوع شاید در نگاه اول عجیب به نظر برسد اما دلیل خوبی برای آن وجود دارد. در نظر گرفتن نوع‌ها به عنوان مجموعه‌های شامل مقادیر می‌تواند به درک علت این ممنوعیت کمک کند.

اگر در وضعیتی بودیم که یکی از این دو نوع از دیگری کلی‌تر بود و به عبارت دیگر، یکی از این دو نوع زیرمجموعه‌ی دیگری بود، طبیعتاً‌ بهتر بود که بتوان ترکیبی از این دو مقادیر را در اعمال حسابی به کار گرفت و نتیجه‌ی تولید شده از نوع کلی‌تر در نظر گرفته شود. به عنوان یک مثال خارج از بحث، می‌توانیم اعداد صحیح و حقیقی را در نظر بگیریم. از آنجایی که اعداد صحیح زیرمجموعه‌ی اعداد حقیقی هستند و به عبارتی دیگر، هر عدد صحیح یک عدد حقیقی هم هست، می‌توانیم یک عدد صحیح را با (در) یک عدد حقیقی جمع (ضرب) کنیم و حاصل را یک عدد اعشاری در نظر بگیریم. اما در اینجا هیچ یک از این دو نوع زیرمجموعه‌ی دیگری نیست. نوع bigint اعداد صحیح بزرگی دارد که در نوع number وجود ندارد و نوع number شامل اعداد اعشاری است که در نوع bigint وجود ندارد. به همین دلیل، کاملاً‌ طبیعی است که امکان استفاده از مقادیر این دو نوع به صورت همزمان به عنوان عملوند در یک محاسبه وجود نداشته باشد.

البته بر خلاف عملگرهای حسابی، این ممنوعیت در مورد عملگرهای مقایسه‌ای وجود ندارد و این عملگرها با مقادیر عددی ترکیبی کار می‌کنند.

> 1 < 2n // => true
> 2 > 1n // => true
> 0 == 0n // => true
> 0 === 0n // => false:
          

نکته‌ی پایانی که باید در مورد مقادیر bigint بدانید این است که عملگرهای بیتی (bitwise operators) نیز روی عملوندهای bigint کار می‌کنند اما هیچ یک از توابع شیء Math عملوندهای bigint را نمی‌پذیرند.