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 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 datos
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
NumberaBigIntcorrectamente, pero elNumberya había perdido precisión previamente - Pasas el
BigIntaNumberpero pierdes precisión en el proceso, porqueNumberno 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) ❌
