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
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 2
53
-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
aBigInt
correctamente, pero elNumber
ya había perdido precisión previamente - Pasas el
BigInt
aNumber
pero pierdes precisión en el proceso, porqueNumber
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 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) ❌