El tipo BigInt

Números grandes en Javascript

En capítulos anteriores hablamos de Rangos numéricos y la problemática de que ciertos valores extremos, muy grandes o muy pequeños, no se pueden representar en Javascript (ni en otros lenguajes de programación), debido a que la información pierde precisión.

¿Qué es BigInt?

BigInt es un tipo de dato de Javascript que nace con la idea de permitir representar valores muy grandes, de una forma relativamente sencilla y compatible con lo que ya existe.

Constructor Descripción
BigInt(number) Devolvemos un número BigInt a partir de un number pasado por parámetro.
number+n Simplemente, añadir una n al final del número. Notación preferida.

La problemática que existe actualmente con el tipo de dato Number es que valores más grandes de 253-1 no pueden ser representados, ya que superarían el límite seguro Number.MAX_SAFE_INTEGER y algunos resultados podrían perder precisión, como se puede ver en el siguiente ejemplo:

const number = 2 ** 53;

number.constructor.name; // "Number" (Es de tipo Number)

number; // 9007199254740992
number + 1; // 9007199254740992
number + 5; // 9007199254740996
number + 40; // 9007199254741032

Para poder realizar estas tareas de forma segura, podemos utilizar el tipo de dato BigInt simplemente añadiendo una n al final del número:

const number = 2n ** 53n;

number.constructor.name; // "BigInt" (Es de tipo BigInt)

number; // 9007199254740992n
number + 1n; // 9007199254740993n
number + 5n; // 9007199254740997n
number + 40n; // 9007199254741032n

Vamos a ver ahora como trabajar con números BigInt correctamente.

Mezclando tipos de dato

Observa que en estos ejemplos anteriores estabamos trabajando con tipos BigInt en ambos operandos y no los estamos mezclando en ningún caso con tipos Number. Hay que tener varios detalles en cuenta a la hora de trabajar con BigInt, porque nos pueden surgir dudas:

const number = 5 + 5n;          // ERROR: Cannot mix BigInt and other types, use explicit conversions
const number = BigInt(5) + 5n // 10n (Ok, convierte a BigInt y realiza la operación)
const number = 5 + Number(5n); // 10 (Ok, convierte a Number y realiza la operación)

Sin embargo, ten muy en cuenta que no se recomienda hacer conversiones entre Number y BigInt salvo que tengas muy controlado lo que vas a hacer, ya que podrías estar en una de estas situaciones:

  • Pasas el Number a BigInt correctamente, pero el Number ya había perdido precisión previamente
  • Pasas el BigInt a Number pero pierdes precisión en el proceso, porque Number no puede representarlo

Ten en cuenta que si comparamos un Number con un BigInt con el operador de identidad (===) nos dará falso aunque sea igual, ya que son dos tipos de datos diferentes. Sin embargo, con el operador de igualdad (==) si nos dará verdadero:

5n === 5;     // false (el valor es igual, el tipo de dato no)
5n == 5; // true (el valor es igual)

String(2n ** 53n); // "9007199254740992" (le quita la n)
(2n ** 53n).toString(); // "9007199254740992" (le quita la n)

Por otro lado, si convertimos a un , retirará la n y se mostrará representado como un número normal.

Métodos de BigInt

El tipo de dato BigInt tiene dos métodos estáticos, que nos pueden servir para operar a nivel de bits. Disponemos de los métodos .asIntN() y .asUintN():

Método Descripción
BigInt.asIntN(bits, bigNumber) Ajusta bigNumber a bits (devuelve entero con signo).
BigInt.asUintN(bits, bigNumber) Ajusta bigNumber a bits (devuelve entero sin signo).

Vamos a verlos ilustrados en un ejemplo para comprender como funcionan.

En primer lugar, .asIntN() significa as (Signed) Integer Number. En el ejemplo vamos a usar 2 bits para representar un número con signo. Con 2 bits podemos representar 00, 01, 10 y 11, por lo que sólo podemos representar 4 números (en nuestro caso desde -2 hasta 1):

// Con 2 bits con signo puedes representar desde (-2 hasta 1)
BigInt.asIntN(2, -3n); // -3n (Representa el -3 con 2 bits) ❌
BigInt.asIntN(2, -2n); // -2n (Representa el -2 con 2 bits) ✅ 10
BigInt.asIntN(2, -1n); // -1n (Representa el -1 con 2 bits) ✅ 11
BigInt.asIntN(2, 0n); // 0n (Representa el 0 con 2 bits) ✅ 00
BigInt.asIntN(2, 1n); // 1n (Representa el 1 con 2 bits) ✅ 01
BigInt.asIntN(2, 2n); // -2n (Representa el 2 con 2 bits) ❌
BigInt.asIntN(2, 3n); // -1n (Representa el 3 con 2 bits) ❌

En segundo lugar, .asUintN significa as Unsigned Integer Number. En el ejemplo vamos a usar 2 bits para representar un número sin signo. Con 2 bits podemos representar 00, 01, 10 y 11, y como no tenemos signo y no debemos preocuparnos de los números negativos, tenemos más disponible para los números positivos. Podremos representar 4 números (en nuestro caso desde 0 hasta 4):

// Con 2 bits sin signo puedes representar desde (0 hasta 4)
BigInt.asUintN(2, -2n); // 2n (Representa el -2 con 2 bits) ❌
BigInt.asUintN(2, -1n); // 3n (Representa el -1 con 2 bits) ❌
BigInt.asUintN(2, 0n); // 0n (Representa el 0 con 2 bits) ✅ 00
BigInt.asUintN(2, 1n); // 1n (Representa el 1 con 2 bits) ✅ 01
BigInt.asUintN(2, 2n); // 2n (Representa el 2 con 2 bits) ✅ 10
BigInt.asUintN(2, 3n); // 3n (Representa el 3 con 2 bits) ✅ 11
BigInt.asUintN(2, 4n); // 0n (Representa el 4 con 2 bits) ❌
Tabla de contenidos