Все числа в JavaScript хранятся в 64-битном формате IEEE-754, который также называют «числа с плавающей точкой двойной точности» (double precision floating point numbers).
Способы записи числа в JavaScript
Для укороченной записи больших чисел в JS используется буква "e", которая добавляется к числу и заменяет указанное количество нулей:
1 2 3 |
let billion = 1e9; // 1 миллиард (1 и 9 нулей) alert( 7.3e9 ); // 7.3 миллиардов (7,300,000,000) |
Другими словами, "e" производит операцию умножения числа на 1 с указанным количеством нулей.
Для укороченной записи малых чисел в JS также используется буква "e", которая добавляется к числу и заменяет указанное количество нулей:
1 2 |
let ms = 0.000001; // микросекунда, шесть нулей слева от 1 let ms = 1e-6; // микросекунда, шесть нулей слева от 1 |
Шестнадцатеричные, двоичные и восьмеричные числа в JavaScript
Шестнадцатеричные числа широко используются в JavaScript для представления цветов, кодировки символов и многого другого.
Короткий стиль записи шестнадцатеричного числа:
- 0x, после которого указывается число.
Например:
1 2 |
alert( 0xff ); // 255 alert( 0xFF ); // 255 (тоже самое, регистр не имеет значения) |
Не так часто используются двоичные и восьмеричные числа, но они также поддерживаются 0b для двоичных и 0o для восьмеричных.
Короткий стиль записи двоичного числа:
1 |
let a = 0b11111111; // бинарная форма записи числа 255 |
Короткий стиль записи восьмеричного числа:
1 |
let b = 0o377; // восьмеричная форма записи числа 255 |
Простые математические действия в JS
1 2 3 4 5 6 7 8 |
value = num1 + num2; value += 100; // увеличение на 100 value = num1 % 4; // остаток от деления (30/4=7 ост.2) value++; // увеличить значение на единицу (постфиксный инкремент, постинкремент) со следующей строки ++value; // увеличить значение на единицу (префиксный инкремент, преинкремент) на этой строке value--; // уменьшить значение на единицу (постдекремент) со следующей строки --value; // уменьшить значение на единицу (предекремент) на этой строке |
Перевод числа в строку (метод toString(base) )
Метод num.toString(base) возвращает строковое представление числа num в системе счисления base.
1 2 3 4 |
let num = 255; alert( num.toString(16) ); // ff alert( 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-разрядную систему счисления можно использовать для получения короткого представления большого числового идентификатора. К примеру, для создания короткой ссылки. Для этого просто преобразуем его в 36-разрядную систему счисления:
1 2 3 4 5 |
alert( 123456..toString(36) ); // 2n9c // ИЛИ (123456).toString(36); // 2n9c |
Внимание! Две точки в 123456..toString(36) это не опечатка. Если нам надо вызвать метод непосредственно на числе, как toString в примере выше, то нам надо поставить две точки .. после числа. Если мы поставим одну точку: 123456.toString(36), тогда это будет ошибкой, поскольку синтаксис JavaScript предполагает, что после первой точки начинается десятичная часть. А если поставить две точки, то JavaScript понимает, что десятичная часть отсутствует, и начинается метод.
Потеря точности при расчетах в JavaScript (неточные вычисления)
Внутри JavaScript число представлено в виде 64-битного формата IEEE-754. Для хранения числа используется 64 бита: 52 из них используется для хранения цифр, 11 из них для хранения положения десятичной точки (если число целое, то хранится 0), и один бит отведён на хранение знака.
Если число слишком большое, оно переполнит 64-битное хранилище, JavaScript вернёт бесконечность:
1 |
alert( 1e500 ); // Infinity |
Наиболее часто встречающаяся ошибка при работе с числами в JavaScript – это потеря точности:
1 2 3 4 5 6 |
alert( 0.1 + 0.2 == 0.3 ); // false, т.к. 0.1 + 0.2 == 0.30000000000000004 alert( 0.1 + 0.2 ); // 0.30000000000000004 // ИЛИ alert( 9999999999999999 ); // выведет 10000000000000000 |
Число хранится в памяти в бинарной форме, как последовательность бит – единиц и нулей. Но дроби, такие как 0.1, 0.2, которые выглядят довольно просто в десятичной системе счисления, на самом деле являются бесконечной дробью в двоичной форме.
Другими словами, что такое 0.1? Это единица делённая на десять — 1/10, одна десятая. В десятичной системе счисления такие числа легко представимы, по сравнению с одной третьей: 1/3, которая становится бесконечной дробью 0.33333(3).
Деление на 10 гарантированно хорошо работает в десятичной системе, но деление на 3 – нет. По той же причине и в двоичной системе счисления, деление на 2 обязательно сработает, а 1/10 становится бесконечной дробью.
В JavaScript нет возможности для хранения точных значений 0.1 или 0.2, используя двоичную систему, точно также, как нет возможности хранить одну третью в десятичной системе счисления.
Числовой формат IEEE-754 решает эту проблему путём округления до ближайшего возможного числа. Правила округления обычно не позволяют нам увидеть эту «крошечную потерю точности», но она существует.
Ошибка в точности вычислений для чисел с плавающей точкой сохраняется в любом другом языке, где используется формат IEEE 754, включая PHP, Java, C, Perl, Ruby.
Для устранения проблемы необходимо округлить результат вычислений, используя метод toFixed(n):
1 2 |
let sum = 0.1 + 0.2; alert( sum.toFixed(2) ); // 0.30 |
Метод умножения и последующего деления уменьшает погрешность, но полностью её не решает.
Проверка значений на тип number (isFinite и isNaN)
Числовые значения Infinity (и -Infinity) , а также NaN принадлежат типу number, но не являются «обычными» числами, поэтому в JS есть функции для их проверки:
- isNaN(value) преобразует значение в число и проверяет является ли оно NaN;
- isFinite(value) преобразует аргумент в число и возвращает true, если оно является обычным числом, т.е. не NaN/Infinity/-Infinity.
1 2 3 4 5 6 7 8 |
alert( isNaN(NaN) ); // true alert( isNaN("str") ); // true alert( NaN === NaN ); // false (т.к. NaN не является равным ни чему другому, даже самому себе) alert( isFinite("15") ); // true alert( isFinite("str") ); // false, потому что специальное значение: NaN alert( isFinite(Infinity) ); // false, потому что специальное значение: Infinity |
Иногда isFinite используется для проверки, содержится ли в строке число:
1 2 |
let num = +prompt("Enter a number", ''); alert( isFinite(num) ); |
Помните, что пустая строка интерпретируется как 0 во всех числовых функциях, включая isFinite.
Сравнение Object.is
Существует специальный метод Object.is, который сравнивает значения примерно как ===, но более надёжен в двух особых ситуациях:
1 2 3 4 5 |
Object.is(NaN, NaN) // === true, т.е. он работает с NaN Object.is(0, -0) // === false, технически эти значения разные Object.is(a, b) // идентичен a === b |
Явное преобразования чисел (функции parseInt(str, radix) и parseFloat)
Для явного преобразования к числу можно использовать унарный "+" или Number(). Если строка не является в точности числом, то результат будет NaN:
1 |
alert( +"100px" ); // NaN |
Единственное исключение — это пробелы в начале строки и в конце, они игнорируются.
Функции parseInt и parseFloat предназначены для получения числового значения из смешанных строк. Они «читают» число из строки и если в процессе чтения возникает ошибка, то возвращают полученное до ошибки число:
- parseInt возвращает целое число,
- parseFloat возвращает число с плавающей точкой.
1 2 3 4 5 |
alert( parseInt('100px') ); // 100 alert( parseFloat('12.5em') ); // 12.5 alert( parseInt('12.3') ); // 12, вернётся только целая часть alert( parseFloat('12.3.4') ); // 12.3, произойдёт остановка чтения на второй точке |
Если значение параметра string не принадлежит строковому типу, оно преобразуется в него (с помощью абстрактной операции ToString).
Функции parseInt/parseFloat могут интерпретировать только начальную часть строки как численное значение; они вернут NaN, если не смогли прочитать в начале строки ни одну цифру:
1 |
alert( parseInt('a123') ); // NaN, на первом символе происходит остановка чтения |
Второй аргумент parseInt(str, radix)
Функция parseInt() имеет необязательный второй параметр, который определяет систему счисления, таким образом parseInt может также читать строки с шестнадцатеричными числами, двоичными числами и т.д.
Если основание системы счисления не определено или равно 0, предполагается, что оно равно 10, кроме случаев, когда число начинается с пар кодовых единиц 0x или 0X, и в этом случае предполагается, что основание системы счисления равно 16. Если система счисления равна 16, число может также необязательно начинаться с пар кодовых единиц 0x или 0X.
1 2 3 |
alert( parseInt('0xff', 16) ); // 255 alert( parseInt('ff', 16) ); // 255, без 0x тоже работает alert( parseInt('2n9c', 36) ); // 123456 |
Другие математические функции JavaScript (объект Math)
Объект Math является встроенным объектом, хранящим в своих свойствах и методах различные математические константы и функции.
Объект Math не является функциональным объектом. Math не работает с числами типа BigInt.
В отличие от других глобальных объектов, объект Math не является конструктором: все свойства и методы объекта Math являются статическими.
Вы ссылаетесь на константу π через Math.PI и вызываете функцию синуса через Math.sin(x), где x является аргументом метода. Константы в JavaScript определены с полной точностью действительных чисел.
Свойства объекта Math:
1 2 3 4 5 6 7 8 |
Math.E // основание натуральных логарифмов, приблизительно равное 2,718. Math.LN2 // натуральный логарифм из 2, приблизительно равен 0,693. Math.LN10 // натуральный логарифм из 10, приблизительно равен 2,303. Math.LOG2E // двоичный логарифм из E, приблизительно равен 1,443. Math.LOG10E // десятичный логарифм из E, приблизительно равен 0,434. Math.PI // отношение длины окружности круга к его диаметру, приблизительно равно 3,14159. Math.SQRT1_2 // квадратный корень из 1/2; или, что тоже самое, 1, делённая на квадратный корень из 2, приблизительно равен 0,707. Math.SQRT2 // квадратный корень из 2, приблизительно равен 1,414. |
Методы объекта Math:
1 2 3 4 5 6 |
value = Math.random(); // случайное число value = Math.round(3.578); // 4 (округление до ближайшего целого по правилам математики) value = Math.ceil(3.576); // 4 (округление до ближайшего целого в большую сторону) value = Math.floor(3.576); // 3 (округление до ближайшего целого в меньшую сторону) value = Math.min(12, 45, 767, 4); // 4 value = Math.pow(2, 10); // 2 в степени 10 = 1024 |
Подробнее об объекте Math, его свойствах и всех методах
Округление чисел в JavaScript (floor, ceil, round, trunc, toFixed)
Встроенные функции JavaScript для округления чисел до целого:
- Math.floor - округление в меньшую сторону.
- Math.ceil - округление в большую сторону.
- Math.round - округление до ближайшего целого.
- Math.trunc (не поддерживается в Internet Explorer) - удаление дробной части без округления.
Метод JavaScript toFixed(m) для округления чисел до заданной точности:
- num.toFixed(m) - округляет значение до ближайшего числа (m - число знаков после запятой), как в большую, так и в меньшую сторону, аналогично методу Math.round. Результат возвращает в виде строки.
1 2 3 4 5 6 |
value = 0.6 + 0.7; // результат 1.2999999999999998 value = value.toFixed(2); // результат 1.30 - строка (string) value1 = 0.6 + 0.7; // результат 1.2999999999999998 value1 = +value1.toFixed(2); // результат 1.3 - число (number), приведение с помощью унарного "+" value1 = parseFloat(value1.toFixed(2)); // результат 1.3 - число (number), приведение с помощью функции parseFloat |
Если десятичная часть короче, чем необходимо, в конец строки будут добавлены нули:
1 2 |
let num = 12.34; alert( num.toFixed(5) ); // "12.34000", добавлены нули, чтобы получить 5 знаков после запятой |
Например, чтобы округлить число до второго знака после запятой, мы можем умножить число на 100, вызвать функцию округления и разделить обратно.
1 2 3 |
let num = 1.23456; alert( Math.floor(num * 100) / 100 ); // 1.23456 -> 123.456 -> floor -> 123 -> 1.23 |