Основным числовым типом в JavaScript является тип Number, используемый для представления целых чисел и приближенных действительных чисел. JavaScript использует 64-битный формат с плавающей запятой, определенный стандартом IEEE 754, для представления числовых значений. Это означает, что JavaScript может представлять максимальное целое число ±1,797 693 134 862 315 7 × 10^308 и минимальное целое число ±5 × 10^-324.

Этот числовой формат в JavaScript позволяет нам точно представлять все целые числа от -9 007 199 254 740 992 (-2⁵³) до 9 007 199 254 740 992 (2⁵³) включительно. Если ваше значение выходит за пределы этого диапазона, вы можете столкнуться с некоторой потерей точности младших цифр.

Когда числовое значение появляется непосредственно в программе JavaScript, оно называется «числовым литералом». Обратите внимание, что любой числовой литерал может иметь префикс со знаком минус (-), чтобы сделать значение отрицательным.

1.Целочисленные литералы.

В программах JavaScript десятичные целые числа с основанием 10 можно записать непосредственно в виде последовательности цифр. Например:

0
3
10000000

Помимо десятичных целочисленных литералов, JavaScript также поддерживает шестнадцатеричные значения (по основанию 16). Шестнадцатеричные литералы начинаются с 0x или 0X, за которыми следует строка шестнадцатеричных цифр. Шестнадцатеричные цифры состоят из цифр от 0 до 9 и букв от a (или A) до f (или F), где от a до f представляют числа от 10 до 15. Вот примеры шестнадцатеричных целочисленных литералов:

//=> 255: (15*16 + 15)
0xff
//=> 195939070
0xBADCAFE

В ES6 и более поздних версиях вы также можете представлять целые числа в двоичном (основание 2) или восьмеричном (основание 8) с помощью префиксов 0b и 0o (или 0B и 0O):

//=> 21: (1*16 + 0*8 + 1*4 + 0*2 + 1*1)
0b10101
//=> 255: (3*64 + 7*8 + 7*1)
0o377

(Мягкие навыки для программистов: amazon.com/dp/B0CF599P8J)

2. Литералы с плавающей запятой.

Литералы с плавающей запятой могут включать десятичную точку и следовать традиционному синтаксису действительных чисел. Действительное значение состоит из целой части числа, десятичной точки и дробной части числа.

Литералы с плавающей запятой также могут использовать экспоненциальную запись, где за действительным значением следует буква e (или E), необязательный знак плюс или минус и целочисленный показатель степени. Это обозначение представляет собой реальное значение, умноженное на 10, возведенное в степень экспоненты.

Более краткая форма синтаксиса:

[digits][.digits][(E|e)[(+|-)]digits]

Например:

3.14
2345.6789
.333333333333333333
//6.02 x 10^23
6.02e23
//1.4738223 x 10^-32
1.4738223E-32

Вы можете использовать символы подчеркивания для разделения разделов числовых литералов. Эта функция перешла на более поздние стадии стандартизации и поддерживается всеми основными браузерами и реализациями Node:

// Using underscores as a thousands separator
let billion = 1_000_000_000;
// Using underscores as a byte separator
let bytes = 0x89_AB_CD_EF;
// Using underscores as a nibble separator
let bits = 0b0001_1101_0111;
// Can also be used in the fractional part
let fraction = 0.123_456_789;

(Мягкие навыки для программистов: amazon.com/dp/B0CF599P8J)

3. Арифметика в JavaScript.

Программы JavaScript используют арифметические операторы, предоставляемые языком, для управления числовыми значениями. К этим операторам относятся сложение +, вычитание -, умножение *, деление / и по модулю (остаток после деления) %. В ES2016 появился оператор возведения в степень **.

В дополнение к этим основным арифметическим операторам JavaScript также предоставляет набор функций и констант через объект Math для поддержки более сложных математических вычислений:

// => 9007199254740992: 2 raised to the power of 53
Math.pow(2, 53)
// => 1.0: rounded to the nearest integer
Math.round(0.6)
// => 1.0: rounded up to the nearest integer
Math.ceil(0.6)
// => 0.0: rounded down to the nearest integer
Math.floor(0.6)
// => 5: absolute value
Math.abs(-5)
// returns the maximum of the provided arguments
Math.max(x, y, z)
// returns the minimum of the provided arguments
Math.min(x, y, z)
// pseudo-random number x, where 0 <= x < 1.0
Math.random()
// π: constant representing the mathematical constant pi
Math.PI
// e: constant representing the base of the natural logarithm
Math.E
// => 3**0.5: square root of 3
Math.sqrt(3)
// => 3**(1/3): cube root of 3
Math.pow(3, 1/3)
// trigonometric functions: also Math.cos, Math.atan, etc.
Math.sin(0)
// natural logarithm of 10
Math.log(10)
// logarithm base 10 of 100
Math.log(100) / Math.LN10
// logarithm base 2 of 512
Math.log(512) / Math.LN2
// cube of Math.E
Math.exp(3)

ES6 представил новый набор функций для объекта Math:

// => 3: cube root
Math.cbrt(27)
// => 5: square root of the sum of squares of all arguments
Math.hypot(3, 4)
// => 2: base 10 logarithm
Math.log10(100)
// => 10: base 2 logarithm
Math.log2(1024)
// natural logarithm of (1+x): precise for very small x
Math.log1p(x)
// Math.exp(x) - 1; inverse of Math.log1p()
Math.expm1(x)
// returns -1, 0, or 1 depending on the sign of the argument
Math.sign(x)
// => 6: optimized 32-bit integer multiplication
Math.imul(2, 3)
// => 28: number of leading zero bits in the 32-bit binary representation
Math.clz32(0xf)
// => 3: removes the fractional part to get an integer
Math.trunc(3.9)
// rounds to the nearest 32-bit floating point value
Math.fround(x)
// hyperbolic sine, also Math.cosh() and Math.tanh()
Math.sinh(x)
// hyperbolic arcsine, also Math.acosh() and Math.atanh()
Math.asinh(x)

Арифметика в JavaScript не выдает ошибок при переполнении, опустошении или делении на ноль. Когда результат числовой операции превышает представимый диапазон (переполнение), результатом является специальное бесконечное значение Infinity. Аналогично, когда абсолютное значение отрицательного числа превышает представимый диапазон отрицательных чисел (недополнение), результатом будет отрицательная бесконечность -Infinity. Поведение этих двух бесконечных значений соответствует ожиданиям: любая арифметическая операция, включающая сложение, вычитание, умножение или деление с бесконечностью, приводит к бесконечности (хотя знак может измениться).

Недополнение происходит, когда результат числовой операции приближается к нулю, но все еще больше наименьшего представимого числа. В этом случае JavaScript возвращает 0. Если в случае обратного значения происходит нижнее переполнение, JavaScript возвращает специальное значение, известное как «отрицательный ноль». Это значение почти неотличимо от обычного нуля и редко вызывает беспокойство у программистов JavaScript.

Деление на ноль не является ошибкой в ​​JavaScript; это просто приводит к бесконечности или отрицательной бесконечности. Однако есть исключение: при делении 0 на 0 получается особое значение, называемое «Не число» (NaN). Кроме того, такие операции, как деление бесконечности на бесконечность, квадратный корень из отрицательного числа или использование NaN в качестве операнда в арифметических операциях, в результате дают NaN.

JavaScript предопределяет глобальные константы Infinity и NaN так, чтобы они соответствовали положительной бесконечности и не числу соответственно. Доступ к этим значениям также можно получить через свойства объекта Number:

// Positive infinity due to being too large to represent
Infinity
// Same as above
Number.POSITIVE_INFINITY
// => Infinity
1 / 0
// => Infinity; overflow
Number.MAX_VALUE * 2
// Negative infinity due to being too large to represent as a negative number
-Infinity
// Same as above
Number.NEGATIVE_INFINITY
// => -Infinity
-1 / 0
// => -Infinity
-Number.MAX_VALUE * 2
​
// NaN: Not a Number
NaN
// Same as above, different syntax
Number.NaN
// => NaN
0 / 0
// => NaN
Infinity / Infinity
​
// => 0: underflow
Number.MIN_VALUE / 2
// => -0: negative zero
-Number.MIN_VALUE / 2
// => -0: also negative zero
-1 / Infinity
//
-0

ES6 представил следующие свойства объекта Number:

// Same as global parseInt() function
Number.parseInt()
// Same as global parseFloat() function
Number.parseFloat()
// Check if x is NaN
Number.isNaN(x)
// Check if x is a finite number
Number.isFinite(x)
// Check if x is an integer
Number.isInteger(x)
//
Number.isSafeInteger(x)
// => -(2**53 - 1)
Number.MIN_SAFE_INTEGER
// => 2**53 - 1
Number.MAX_SAFE_INTEGER
// => 2**-52: smallest difference between two representable numbers
Number.EPSILON

NaN имеет необычную характеристику в JavaScript: он не равен и не равен никакому значению, включая самого себя. Это означает, что вы не можете определить, является ли переменная x значением NaN, используя сравнение x === NaN. Вместо этого вы должны использовать x != x или Number.isNaN(x) для проверки NaN. Оба этих выражения возвращают true только в том случае, если x имеет то же значение, что и глобальная константа NaN.

Глобальная функция isNaN() аналогична Number.isNaN(). Он возвращает true, если аргумент имеет значение NaN или когда аргумент представляет собой нечисловое значение, которое невозможно преобразовать в число. Связанная функция Number.isFinite() возвращает true, если аргумент не равен NaN, Infinity или -Infinity. Глобальная функция isFinite() возвращает true, если аргумент представляет собой конечное число или значение, которое можно преобразовать в конечное число.

Отрицательный ноль ведет себя несколько необычно. Он считается равным положительному нулю (даже при использовании строгого сравнения на равенство в JavaScript), а это означает, что, помимо его роли делителя, сложно различить эти два значения:

// Regular zero
let zero = 0;
// Negative zero
let negz = -0;
// => true: zero equals negative zero
zero === negz
// => false: Infinity does not equal negative infinity
1 / zero === 1 / negz

(Мягкие навыки для программистов: amazon.com/dp/B0CF599P8J)

4. Двоичные числа с плавающей запятой и ошибки округления.

Действительных чисел бесконечно много, но формат с плавающей запятой в JavaScript может представлять только их конечное подмножество (точнее, он может представлять до 18 437 736 874 454 810 627 различных значений). Это означает, что когда вы работаете с действительными числами в JavaScript, числовые значения, которыми вы манипулируете, часто приближаются к фактическим значениям.

JavaScript (как и все современные языки программирования) использует представление с плавающей запятой IEEE-754, которое является двоичным представлением. Это представление может точно представлять такие дроби, как 1/2, 1/8 и 1/1024. Однако наиболее распространенными дробями, которые мы используем (особенно в финансовых расчетах), являются десятичные дроби, такие как 1/10, 1/100 и так далее. Двоичное представление с плавающей запятой не может точно представлять даже простые числа, такие как 0,1.

Хотя числовые значения JavaScript имеют достаточную точность, чтобы очень близко приближаться к 0,1, они не могут точно ее представить. Это может привести к проблемам, как показано в следующем коде:

// 30 cents minus 20 cents
let x = 0.3 - 0.2;
// 20 cents minus 10 cents
let y = 0.2 - 0.1;
// => false: These two values are not equal!
x === y
// => false: .3 - .2 is not equal to .1
x === 0.1
// => true: .2 - .1 is equal to .1
y === 0.1

Из-за ошибок округления разница между приблизительными значениями 0,3 и 0,2 не равна разнице между приблизительными значениями 0,2 и 0,1. Это не проблема, специфичная для JavaScript; это общая проблема всех языков программирования, использующих двоичные числа с плавающей запятой. Также важно отметить, что значения x и y в приведенном выше коде очень близки и очень близки к правильным значениям. Рассчитанные значения идеально подходят для большинства целей, но пытаться сравнить их на предмет точного равенства не рекомендуется.

Если приблизительные значения с плавающей запятой создают проблемы в вашей программе, вы можете рассмотреть возможность использования эквивалентных целых чисел. Например, при расчетах, связанных с деньгами, вы можете работать с целочисленными представлениями центов, а не с долями доллара.

(Мягкие навыки для программистов: amazon.com/dp/B0CF599P8J)

5. Представление целых чисел произвольной точности с помощью Bigint.

В ES2020 в JavaScript появился новый числовой тип BigInt. По состоянию на начало 2020 года этот тип реализован в Chrome, Firefox, Edge и Node.js, над ним также работает Safari. Как следует из названия, значения BigInt являются целыми числами. Основной мотивацией для добавления этого типа было представление 64-битных целых чисел, что имеет решающее значение для совместимости со многими другими языками и API. Однако значения BigInt могут состоять из тысяч или даже миллионов цифр, что удовлетворяет потребности в больших числах (хотя реализации BigInt не подходят для шифрования, поскольку не учитывают защиту от атак по времени).

Литералы BigInt записываются как последовательность цифр, за которой следует строчная буква «n». По умолчанию система счисления равна 10, но вы можете представлять двоичные, восьмеричные и шестнадцатеричные числа BigInt, используя префиксы 0b, 0o и 0x соответственно:

// A not-so-large BigInt literal
1234n
// Binary BigInt
0b111111n
// Octal BigInt
0o7777n
// => 2n ** 63n: A 64-bit integer
0x8000000000000000n

Вы можете преобразовать обычные числа или строки JavaScript в значения BigInt с помощью функции BigInt():

// => 9007199254740991n
BigInt(Number.MAX_SAFE_INTEGER)
// A 1 followed by 100 zeros
let string = "1" + "0".repeat(100);
// => 10n ** 100n: An astronomical number
BigInt(string)

Арифметические операции со значениями BigInt аналогичны обычным числовым операциям JavaScript, за исключением того, что при делении остаток отбрасывается и округляется в сторону нуля:

// => 3000n
1000n + 2000n
// => 1000n
3000n - 2000n
// => 6000000n
2000n * 3000n
// => 3n: Quotient is 3
3000n / 997n
// => 9n: Remainder is 9
3000n % 997n
// A Mersenne prime with 39457 digits
(2n ** 131071n) - 1n

Хотя стандартные операторы +, -, *, /, % и ** работают с BigInt, вы не можете смешивать операнды BigInt с обычными числовыми операндами. Это правило может показаться странным на первый взгляд, но на самом деле оно разумно. Если один числовой тип является более общим, чем другой, можно легко определить вычисления со смешанными операндами и вернуть более общий тип. Однако в данном случае ни один из двух типов не является более общим, чем другой: BigInt может представлять очень большие значения, что делает его более общим, чем обычные числовые типы. Но BigInt может представлять только целые числа, что делает обычные числовые типы JavaScript более общими в этом аспекте. Эту дилемму решить невозможно, поэтому JavaScript просто запрещает смешивать эти два типа операндов при использовании арифметических операторов.

С другой стороны, операторы сравнения позволяют смешивать типы операндов:

// => true
1 < 2n
// => true
2 > 1n
// => true
0 == 0n
// => false: === also checks for type equality
0 == 0n

Побитовые операторы обычно можно использовать с операндами BigInt. Однако ни одна из функций объекта Math не принимает операнды BigInt.

(Мягкие навыки для программистов: amazon.com/dp/B0CF599P8J)

6. Даты и время.

JavaScript предоставляет простой класс Date для представления и управления данными, связанными с датами и временем. Дата в JavaScript — это объект, но он также имеет числовое представление, известное как временная метка, которая представляет собой количество миллисекунд, прошедших с 1 января 1970 года:

// Timestamp (numeric) of the current time
let timestamp = Date.now();
// Date object of the current time
let now = new Date();
// Convert to milliseconds timestamp
let ms = now.getTime();
// Convert to a string in ISO format
let iso = now.toISOString();

(Мягкие навыки для программистов: amazon.com/dp/B0CF599P8J)