BigInt – полное руководство по новому типу данных JavaScript

BigInt позволяет JavaScript- программистам использовать целочисленные значения, превышающие диапазон, поддерживаемый типом данных Number. Благодаря использованию BigInt целочисленное переполнение больше не будет проблемой.

Также с помощью нового типа данным можно безопасно работать с метками времени, большими целочисленными идентификаторами, не прибегая к обходным путям. После добавления в спецификацию BigInt станет вторым числовым типом данных в JavaScript

В этой статье мы рассмотрим тип данных BigInt и узнаем, как он может помочь преодолеть ограничения типа данных Number в JavaScript.

Содержание

Проблема

Многие языки программирования поддерживают несколько числовых типов данных: float, double, integer и bignum. Но в JavaScript все числа представлены в 64-битном формате с плавающей запятой двойной точности, как определено стандартом IEEE 754-2008.

Согласно нему, большие целые числа, которые не могут быть точно представлены, автоматически округляются. Поэтому тип данных Number в JavaScript может безопасно представлять только целые числа от -9007199254740991 (-(253-1)) до 9007199254740991 (253-1). Любое целочисленное значение, выпадающее из этого диапазона, будет округлено.

Это можно легко проверить, выполнив следующий код:

console.log(9999999999999999);    // → 10000000000000000

Это целое число больше максимального значения, которое JavaScript может представить с помощью типа данных  Number. Поэтому оно округлено. Это может поставить под угрозу надежность и безопасность приложения.

Вот еще один пример:

// обратите внимание на последние цифры
9007199254740992 === 9007199254740993;    // → true

JavaScript предоставляет константу Number.MAX_SAFE_INTEGER, которая позволяет быстро получить максимально безопасное целое число. Точно так же вы можете получить минимальное безопасное целое число, используя константу Number.MIN_SAFE_INTEGER:

const minInt = Number.MIN_SAFE_INTEGER;
console.log(minInt);         // → -9007199254740991
console.log(minInt - 5);     // → -9007199254740996
// заметьте, что отображается то же число, что и выше 
console.log(minInt - 4);     // → -9007199254740996

Решение

Для обхода этих ограничений JavaScript- разработчики представляют большие целые числа, используя тип данных String. Например, API Twitter добавляет строковую версию идентификаторов к объектам в ответах, пересылаемых в формате JSON. Также разработан целый ряд библиотек, таких как bignumber.js, чтобы упростить работу с большими целыми числами.

Благодаря типу данных BigInt приложениям больше не требуется обходной путь для безопасного представления целых чисел за пределами Number.MAX_SAFE_INTEGER и Number.MIN_SAFE_INTEGER. Арифметические операции над большими целыми числами теперь могут выполняться в JavaScript без риска потери точности.

Дополнительным преимуществом использования BigInt вместо сторонней библиотеки является лучшая производительность кода во время выполнения.

Чтобы создать BigInt, добавьте n в конец целого числа. Для сравнения:

console.log(9007199254740995n);    // → 9007199254740995n
console.log(9007199254740995);     // → 9007199254740996

Также можно вызвать конструктор BigInt():

BigInt("9007199254740995");    // → 9007199254740995n

Значения BigInt также могут быть записаны в двоичной, восьмеричной или шестнадцатеричной системе исчисления:

// двоичная
console.log(0b100000000000000000000000000000000000000000000000000011n); 
// → 9007199254740995n
// шестнадцатеричная
console.log(0x20000000000003n);
// → 9007199254740995n
// восьмеричная
console.log(0o400000000000000003n);
// → 9007199254740995n
// заметьте, что старая форма восьмеричной записи не поддерживается 
console.log(0400000000000000003n);
// → синтаксическая ошибка

Но вы не сможете использовать оператор строгого равенства для сравнения BigInt с обычным числом, потому что они разного типа данных:

console.log(10n === 10);    // → false
console.log(typeof 10n);    // → bigint
console.log(typeof 10);     // → number

Вместо этого можно использовать оператор равенства, который выполняет неявное преобразование типов данных перед компиляцией:

console.log(10n == 10);    // → true

В BigInt могут использоваться все арифметические операторы, кроме унарного оператора плюс (+):

10n + 20n;    // → 30n
10n - 20n;    // → -10n
+10n;         // → Ошибка: нельзя преобразовать значение BigInt value в number
-10n;         // → -10n
10n * 20n;    // → 200n
20n / 10n;    // → 2n
23n % 10n;    // → 3n
10n ** 3n;    // → 1000n
const x = 10n;
++x;          // → 11n
--x;          // → 9n

Некоторые программы могут полагаться на инвариант, в котором оператор + всегда создаёт Number или выдает исключение. Изменение поведения оператора + также приведет к нарушению кода asm.js.

При использовании BigInt ожидается, что арифметические операторы будут возвращать значение BigInt. Поэтому результат работы оператора деления (/) автоматически округляется до ближайшего целого числа. Например:

25 / 10;      // → 2.5
25n / 10n;    // → 2n

Неявное преобразование типа

При неявном преобразовании типов данных может потеряться информация, поэтому операции между BigInt и Number не допускаются. При операциях с большими целыми числами и чисел с плавающей запятой результирующее значение может быть представлено как BigInt или Number. Рассмотрим следующий пример:

(9007199254740992n + 1n) + 0.5

Результат этого выражения находится вне диапазона BigInt и Number. Число с дробной частью не может быть точно преобразовано в BigInt. И BigInt больше 253 не может быть точно преобразовано в Number.

Из-за этого невозможно выполнять арифметические операции между Number и BigInt. Попытка сделать это вызовет ошибку TypeError:

10 + 10n;    // → TypeError
Math.Max(2n, 4n, 6n);    // → TypeError

Но операторы сравнения не следуют этому правилу:

10n > 5;    // → true

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

BigInt(10) + 10n;    // → 20n
// или
10 + Number(10n);    // → 20

Число BigInt считается истинным значением, если оно не равно 0n:

if (5n) {
    // этот блок кода будет выполнен 
}
if (0n) {
    // а этот нет
}

При сортировке массива, состоящего из элементов BigInt и Number, не происходит явного преобразования типов:

const arr = [3n, 4, 2, 1n, 0, -1n];
arr.sort();    // → [-1n, 0, 1n, 2, 3n, 4]

Битовые операторы, такие как |, &, <<, >> и ^, работают с BigInt так же, как с Number. Смешанные операнды не допускаются. Вот некоторые примеры:

90 | 115;      // → 123
90n | 115n;    // → 123n
90n | 115;     // → TypeError

Конструктор BigInt

BigInt может быть создан с помощью конструктора. Аргумент, переданный BigInt(), автоматически преобразуется в BigInt.

BigInt("10");    // → 10n
BigInt(10);      // → 10n
BigInt(true);    // → 1n

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

BigInt(10.2);     // → RangeError
BigInt(null);     // → TypeError
BigInt("abc");    // → SyntaxError

С числом BigInt, созданным с помощью конструктора, можно выполнять арифметические операции:

BigInt(10) * 10n;    // → 100n

При использовании в качестве операндов оператора строгого равенства числа BigInt, созданные с помощью конструктора, обрабатываются аналогично обычным.

BigInt(true) === 1n;    // → true

Библиотечные функции

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

  • asUintN(width, BigInt): перенос BigInt в границы между 0 и 2width-1
  • asIntN(width, BigInt): перенос BigInt в границы между -2width-1 и 2width-1-1

Эти функции особенно полезны при выполнении 64-битных арифметических операций. Таким образом можно оставаться в пределах желаемого диапазона.

Поддержка браузерами и транспилинг

На момент написания этой статьи Chrome версии 67+ и Opera версии 54+ полностью поддерживали тип данных BigInt. К сожалению, Edge и Safari еще не внедрили его поддержку

Firefox по умолчанию не поддерживает тип данных BigInt. Но его можно включить, установив для параметра javascript.options.bigint значение true с помощью about:config. Актуальный список поддерживаемых браузеров доступен здесь.

К сожалению, транспилинг BigInt является чрезвычайно сложным процессом. На данный момент лучшей альтернативой является использование библиотеки JSBI, которая представляет собой реализацию BigInt на чистом JavaScript.

Эта библиотека предоставляет API, который ведет себя точно так же, как и нативный BigInt. Пример использования JSBI:

import JSBI from './jsbi.mjs';

const b1 = JSBI.BigInt(Number.MAX_SAFE_INTEGER);
const b2 = JSBI.BigInt('10');

const result = JSBI.add(b1, b2);
console.log(String(result));    // → '9007199254741001'

Благодаря JSBI при расширении поддержки типа данных браузерами вам не нужно будет переписывать код. Вместо этого нужно автоматически скомпилировать свой код JSBI в нативный код BigInt с помощью плагина babel.

Заключение

BigInt – это новый тип данных. Он позволяет безопасно выполнять арифметические операции над большими целыми числами, использовать большие целочисленные идентификаторы и делать многое другое без применения сторонних библиотек.

Данная публикация представляет собой перевод статьи «The Essential Guide To JavaScript’s Newest Data Type: BigInt» , подготовленной дружной командой проекта Интернет-технологии.ру

Меню