Español

Una guía completa sobre el tipo BigInt de JavaScript, cubriendo sus características, uso y aplicaciones en el manejo de aritmética de enteros grandes. Aprende a superar las limitaciones de JavaScript y a realizar cálculos complejos con precisión.

JavaScript BigInt: Dominando la Aritmética de Enteros Grandes

JavaScript, aunque es un lenguaje versátil, tiene limitaciones al tratar con enteros muy grandes. El tipo estándar `Number` solo puede representar con precisión enteros hasta un cierto límite, conocido como `Number.MAX_SAFE_INTEGER`. Más allá de este límite, los cálculos se vuelven imprecisos, lo que lleva a resultados inesperados. Aquí es donde BigInt viene al rescate. Introducido en ECMAScript 2020, BigInt es un objeto incorporado que proporciona una forma de representar y manipular enteros de tamaño arbitrario, superando las limitaciones del tipo estándar `Number`.

Entendiendo la Necesidad de BigInt

Antes de BigInt, los desarrolladores de JavaScript tenían que depender de bibliotecas o implementaciones personalizadas para manejar cálculos con enteros grandes. Estas soluciones a menudo conllevaban una sobrecarga de rendimiento y una mayor complejidad. La introducción de BigInt proporcionó una forma nativa y eficiente de trabajar con enteros grandes, abriendo posibilidades para aplicaciones en diversos dominios, incluyendo:

Creando Valores BigInt

Hay dos formas principales de crear valores BigInt en JavaScript:

  1. Usando el constructor `BigInt()`: Este constructor puede convertir un número, una cadena de texto o un valor booleano a un BigInt.
  2. Usando el sufijo `n`: Añadir `n` a un literal de entero crea un BigInt.

Ejemplos:

Usando el constructor `BigInt()`:


const bigIntFromNumber = BigInt(12345678901234567890);
const bigIntFromString = BigInt("98765432109876543210");
const bigIntFromBoolean = BigInt(true); // Resulta en 1n
const bigIntFromFalseBoolean = BigInt(false); // Resulta en 0n

console.log(bigIntFromNumber); // Salida: 12345678901234567890n
console.log(bigIntFromString); // Salida: 98765432109876543210n
console.log(bigIntFromBoolean); // Salida: 1n
console.log(bigIntFromFalseBoolean); // Salida: 0n

Usando el sufijo `n`:


const bigIntLiteral = 12345678901234567890n;
console.log(bigIntLiteral); // Salida: 12345678901234567890n

Nota Importante: No puedes mezclar directamente valores BigInt y Number en operaciones aritméticas. Necesitas convertirlos explícitamente al mismo tipo antes de realizar cálculos. Intentar mezclarlos directamente resultará en un `TypeError`.

Operaciones Aritméticas con BigInt

BigInt soporta la mayoría de los operadores aritméticos estándar, incluyendo:

Ejemplos:


const a = 12345678901234567890n;
const b = 98765432109876543210n;

const sum = a + b;
const difference = a - b;
const product = a * b;
const quotient = a / 2n; // Nota: La división trunca hacia cero
const remainder = a % 7n;
const power = a ** 3n; // La exponenciación funciona como se espera

console.log("Sum:", sum); // Salida: Sum: 111111111011111111100n
console.log("Difference:", difference); // Salida: Difference: -86419753208641975320n
console.log("Product:", product); // Salida: Product: 1219326311370217957951669538098765432100n
console.log("Quotient:", quotient); // Salida: Quotient: 6172839450617283945n
console.log("Remainder:", remainder); // Salida: Remainder: 5n
console.log("Power:", power); // Salida: Power: 187641281029182300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n

Consideraciones Importantes:

Operadores de Comparación

Puedes usar los operadores de comparación estándar (`==`, `!=`, `<`, `>`, `<=`, `>=`) para comparar valores BigInt con otros valores BigInt o incluso con valores Number. Sin embargo, ten cuidado con la posible coerción de tipos.

Ejemplos:


const a = 10n;
const b = 20n;
const c = 10;

console.log(a == b);   // Salida: false
console.log(a != b);   // Salida: true
console.log(a < b);    // Salida: true
console.log(a > b);    // Salida: false
console.log(a <= b);   // Salida: true
console.log(a >= b);   // Salida: false

console.log(a == c);   // Salida: true (coerción de tipo)
console.log(a === c);  // Salida: false (sin coerción de tipo)

Mejor Práctica: Usa la igualdad estricta (`===`) y la desigualdad estricta (`!==`) para evitar la coerción de tipos inesperada al comparar valores BigInt y Number.

Conversión entre BigInt y Number

Aunque las operaciones aritméticas directas entre BigInt y Number no están permitidas, puedes convertir entre los dos tipos. Sin embargo, ten en cuenta la posible pérdida de precisión al convertir un BigInt a un Number si el valor BigInt excede `Number.MAX_SAFE_INTEGER`.

Ejemplos:


const bigIntValue = 9007199254740991n; // Number.MAX_SAFE_INTEGER
const numberValue = Number(bigIntValue); // Convirtiendo BigInt a Number
console.log(numberValue); // Salida: 9007199254740991

const largerBigIntValue = 9007199254740992n; // Excede Number.MAX_SAFE_INTEGER
const largerNumberValue = Number(largerBigIntValue);
console.log(largerNumberValue); // Salida: 9007199254740992 (puede ser impreciso)

const numberToBigInt = BigInt(12345); // Convirtiendo Number a BigInt
console.log(numberToBigInt); // Salida: 12345n

Casos de Uso y Ejemplos

Criptografía

Los algoritmos criptográficos a menudo se basan en números primos muy grandes para la seguridad. BigInt proporciona una forma de representar y manipular estos números de manera eficiente.


// Ejemplo: Generando un par de claves simple (no seguro)
function generateKeyPair() {
  const p = 281n; // Un número primo
  const q = 283n; // Otro número primo
  const n = p * q; // Módulo
  const totient = (p - 1n) * (q - 1n); // Función totient de Euler

  // Elige un e (exponente público) tal que 1 < e < totient y mcd(e, totient) = 1
  const e = 17n;

  // Calcula d (exponente privado) tal que (d * e) % totient = 1
  let d = 0n;
  for (let i = 1n; i < totient; i++) {
    if ((i * e) % totient === 1n) {
      d = i;
      break;
    }
  }

  return {
    publicKey: { n, e },
    privateKey: { n, d },
  };
}

const keyPair = generateKeyPair();
console.log("Public Key:", keyPair.publicKey);
console.log("Private Key:", keyPair.privateKey);

Nota: Este es un ejemplo simplificado solo para fines de demostración. La criptografía del mundo real utiliza números primos mucho más grandes y algoritmos más sofisticados.

Cálculos Financieros

Cuando se trata de grandes sumas de dinero, especialmente en transacciones internacionales, la precisión es crítica. BigInt puede prevenir errores de redondeo y asegurar cálculos precisos.


// Ejemplo: Calculando el interés compuesto
function calculateCompoundInterest(principal, rate, time) {
  const principalBigInt = BigInt(principal * 100); // Convertir a centavos
  const rateBigInt = BigInt(rate * 10000);       // Convertir a diezmilésimas de un por ciento
  const timeBigInt = BigInt(time);

  let amount = principalBigInt;
  for (let i = 0n; i < timeBigInt; i++) {
    amount = amount * (10000n + rateBigInt) / 10000n;
  }

  const amountInDollars = Number(amount) / 100;
  return amountInDollars;
}

const principal = 1000000; // $1,000,000
const rate = 0.05;    // Tasa de interés del 5%
const time = 10;     // 10 años

const finalAmount = calculateCompoundInterest(principal, rate, time);
console.log("Final Amount:", finalAmount); // Salida: Final Amount: 1628894.6267774413 (aproximadamente)

En este ejemplo, convertimos el capital y la tasa a valores BigInt para evitar errores de redondeo durante el cálculo. El resultado se convierte de nuevo a un Number para su visualización.

Trabajando con IDs Grandes

En sistemas distribuidos, generar IDs únicos a través de múltiples servidores puede ser un desafío. Usar BigInt te permite crear IDs muy grandes que es poco probable que colisionen.


// Ejemplo: Generando un ID único basado en la marca de tiempo y el ID del servidor
function generateUniqueId(serverId) {
  const timestamp = BigInt(Date.now());
  const serverIdBigInt = BigInt(serverId);
  const random = BigInt(Math.floor(Math.random() * 1000)); // Añadir un poco de aleatoriedad

  // Combinar los valores para crear un ID único
  const uniqueId = (timestamp << 20n) + (serverIdBigInt << 10n) + random;
  return uniqueId.toString(); // Devolver como cadena para un manejo fácil
}

const serverId = 123; // ID de servidor de ejemplo
const id1 = generateUniqueId(serverId);
const id2 = generateUniqueId(serverId);

console.log("Unique ID 1:", id1);
console.log("Unique ID 2:", id2);

BigInt y JSON

Los valores BigInt no son compatibles nativamente con JSON. Intentar serializar un objeto JavaScript que contiene un BigInt usando `JSON.stringify()` resultará en un `TypeError`. Para manejar valores BigInt al trabajar con JSON, tienes un par de opciones:

  1. Convertir a Cadena: Convierte el BigInt a una cadena de texto antes de serializar. Este es el enfoque más común y directo.
  2. Serialización/Deserialización Personalizada: Usa una función de serialización/deserialización personalizada para manejar los valores BigInt.

Ejemplos:

Convirtiendo a Cadena:


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// Convertir BigInt a cadena antes de serializar
data.id = data.id.toString();

const jsonData = JSON.stringify(data);
console.log(jsonData); // Salida: {"id":"12345678901234567890","name":"Example Data"}

// Al deserializar, necesitarás convertir la cadena de nuevo a un BigInt
const parsedData = JSON.parse(jsonData, (key, value) => {
  if (key === "id") {
    return BigInt(value);
  }
  return value;
});

console.log(parsedData.id); // Salida: 12345678901234567890n

Serialización/Deserialización Personalizada (usando `replacer` y `reviver`):


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// Serialización personalizada
const jsonData = JSON.stringify(data, (key, value) => {
  if (typeof value === 'bigint') {
    return value.toString();
  } else {
    return value;
  }
});

console.log(jsonData);

// Deserialización personalizada
const parsedData = JSON.parse(jsonData, (key, value) => {
    if (typeof value === 'string' && /^[0-9]+$/.test(value)) { //verifica si es un número y una cadena
      try {
        return BigInt(value);
      } catch(e) {
          return value;
      }
    }
    return value;
});

console.log(parsedData.id);

Compatibilidad con Navegadores

BigInt es ampliamente compatible en los navegadores modernos. Sin embargo, es esencial verificar la compatibilidad para navegadores o entornos más antiguos. Puedes usar una herramienta como Can I use para verificar el soporte de los navegadores. Si necesitas dar soporte a navegadores más antiguos, podrías considerar usar un polyfill, pero ten en cuenta que los polyfills pueden afectar el rendimiento.

Consideraciones de Rendimiento

Aunque BigInt proporciona una forma poderosa de trabajar con enteros grandes, es importante ser consciente de las posibles implicaciones de rendimiento.

Por lo tanto, usa BigInt solo cuando sea necesario y optimiza tu código para el rendimiento si estás realizando una gran cantidad de operaciones con BigInt.

Conclusión

BigInt es una valiosa adición a JavaScript, que permite a los desarrolladores manejar la aritmética de enteros grandes con precisión. Al comprender sus características, limitaciones y casos de uso, puedes aprovechar BigInt para construir aplicaciones robustas y precisas en diversos dominios, incluyendo la criptografía, los cálculos financieros y la computación científica. Recuerda considerar la compatibilidad de los navegadores y las implicaciones de rendimiento al usar BigInt en tus proyectos.

Para Explorar Más

Esta guía proporciona una visión general completa de BigInt en JavaScript. Explora los recursos enlazados para obtener información más detallada y técnicas avanzadas.