Português

Um guia completo sobre o tipo BigInt do JavaScript, cobrindo seus recursos, uso e aplicações no tratamento de aritmética com inteiros grandes. Aprenda a superar as limitações do JavaScript e a realizar cálculos complexos com precisão.

JavaScript BigInt: Dominando a Aritmética de Inteiros Grandes

O JavaScript, embora seja uma linguagem versátil, tem limitações ao lidar com inteiros muito grandes. O tipo padrão `Number` só consegue representar inteiros com precisão até um certo limite, conhecido como `Number.MAX_SAFE_INTEGER`. Além desse limite, os cálculos tornam-se imprecisos, levando a resultados inesperados. É aqui que o BigInt entra em cena. Introduzido no ECMAScript 2020, o BigInt é um objeto nativo que fornece uma maneira de representar e manipular inteiros de tamanho arbitrário, excedendo as limitações do tipo padrão `Number`.

Entendendo a Necessidade do BigInt

Antes do BigInt, os desenvolvedores JavaScript precisavam contar com bibliotecas ou implementações personalizadas para lidar com cálculos de inteiros grandes. Essas soluções muitas vezes vinham com sobrecarga de desempenho e aumento da complexidade. A introdução do BigInt forneceu uma maneira nativa e eficiente de trabalhar com inteiros grandes, abrindo possibilidades para aplicações em vários domínios, incluindo:

Criando Valores BigInt

Existem duas maneiras principais de criar valores BigInt em JavaScript:

  1. Usando o construtor BigInt(): Este construtor pode converter um número, string ou valor booleano para um BigInt.
  2. Usando o sufixo `n`: Anexar `n` a um literal inteiro cria um BigInt.

Exemplos:

Usando o construtor BigInt():


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

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

Usando o sufixo `n`:


const bigIntLiteral = 12345678901234567890n;
console.log(bigIntLiteral); // Saída: 12345678901234567890n

Nota Importante: Você não pode misturar diretamente valores BigInt e Number em operações aritméticas. Você precisa convertê-los explicitamente para o mesmo tipo antes de realizar os cálculos. Tentar misturá-los diretamente resultará em um `TypeError`.

Operações Aritméticas com BigInt

O BigInt suporta a maioria dos operadores aritméticos padrão, incluindo:

Exemplos:


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

const sum = a + b;
const difference = a - b;
const product = a * b;
const quotient = a / 2n; // Nota: A divisão trunca em direção a zero
const remainder = a % 7n;
const power = a ** 3n; // A exponenciação funciona como esperado

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

Considerações Importantes:

Operadores de Comparação

Você pode usar operadores de comparação padrão (`==`, `!=`, `<`, `>`, `<=`, `>=`) para comparar valores BigInt com outros valores BigInt ou até mesmo com valores Number. No entanto, esteja ciente do potencial de coerção de tipo.

Exemplos:


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

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

console.log(a == c);   // Saída: true (coerção de tipo)
console.log(a === c);  // Saída: false (sem coerção de tipo)

Melhor Prática: Use igualdade estrita (`===`) e desigualdade estrita (`!==`) para evitar coerção de tipo inesperada ao comparar valores BigInt e Number.

Convertendo entre BigInt e Number

Embora operações aritméticas diretas entre BigInt e Number não sejam permitidas, você pode converter entre os dois tipos. No entanto, esteja ciente da perda potencial de precisão ao converter um BigInt para um Number se o valor do BigInt exceder `Number.MAX_SAFE_INTEGER`.

Exemplos:


const bigIntValue = 9007199254740991n; // Number.MAX_SAFE_INTEGER
const numberValue = Number(bigIntValue); // Convertendo BigInt para Number
console.log(numberValue); // Saída: 9007199254740991

const largerBigIntValue = 9007199254740992n; // Excede Number.MAX_SAFE_INTEGER
const largerNumberValue = Number(largerBigIntValue);
console.log(largerNumberValue); // Saída: 9007199254740992 (pode ser impreciso)

const numberToBigInt = BigInt(12345); // Convertendo Number para BigInt
console.log(numberToBigInt); // Saída: 12345n

Casos de Uso e Exemplos

Criptografia

Algoritmos criptográficos geralmente dependem de números primos muito grandes para segurança. O BigInt fornece uma maneira de representar e manipular esses números eficientemente.


// Exemplo: Gerando um par de chaves simples (inseguro)
function generateKeyPair() {
  const p = 281n; // Um número primo
  const q = 283n; // Outro número primo
  const n = p * q; // Módulo
  const totient = (p - 1n) * (q - 1n); // Função totiente de Euler

  // Escolha um e (expoente público) tal que 1 < e < totient e mdc(e, totient) = 1
  const e = 17n;

  // Calcule d (expoente 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 é um exemplo simplificado apenas para fins de demonstração. A criptografia do mundo real usa números primos muito maiores e algoritmos mais sofisticados.

Cálculos Financeiros

Ao lidar com grandes somas de dinheiro, especialmente em transações internacionais, a precisão é crítica. O BigInt pode evitar erros de arredondamento e garantir cálculos precisos.


// Exemplo: Calculando juros compostos
function calculateCompoundInterest(principal, rate, time) {
  const principalBigInt = BigInt(principal * 100); // Converter para centavos
  const rateBigInt = BigInt(rate * 10000);       // Converter para décimos de milésimo de porcento
  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;    // Taxa de juros de 5%
const time = 10;     // 10 anos

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

Neste exemplo, convertemos o principal e a taxa para valores BigInt para evitar erros de arredondamento durante o cálculo. O resultado é então convertido de volta para um Number para exibição.

Trabalhando com IDs Grandes

Em sistemas distribuídos, gerar IDs únicos em múltiplos servidores pode ser um desafio. Usar BigInt permite criar IDs muito grandes que dificilmente colidirão.


// Exemplo: Gerando um ID único baseado no timestamp e ID do servidor
function generateUniqueId(serverId) {
  const timestamp = BigInt(Date.now());
  const serverIdBigInt = BigInt(serverId);
  const random = BigInt(Math.floor(Math.random() * 1000)); // Adicionar um pouco de aleatoriedade

  // Combinar os valores para criar um ID único
  const uniqueId = (timestamp << 20n) + (serverIdBigInt << 10n) + random;
  return uniqueId.toString(); // Retornar como string para facilitar o manuseio
}

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

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

BigInt e JSON

Valores BigInt não são suportados nativamente pelo JSON. Tentar serializar um objeto JavaScript contendo um BigInt usando `JSON.stringify()` resultará em um `TypeError`. Para lidar com valores BigInt ao trabalhar com JSON, você tem algumas opções:

  1. Converter para String: Converta o BigInt para uma string antes de serializar. Esta é a abordagem mais comum e direta.
  2. Serialização/Desserialização Personalizada: Use uma função de serialização/desserialização personalizada para lidar com valores BigInt.

Exemplos:

Convertendo para String:


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

// Converter BigInt para string antes de serializar
data.id = data.id.toString();

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

// Ao desserializar, você precisará converter a string de volta para um BigInt
const parsedData = JSON.parse(jsonData, (key, value) => {
  if (key === "id") {
    return BigInt(value);
  }
  return value;
});

console.log(parsedData.id); // Saída: 12345678901234567890n

Serialização/Desserialização Personalizada (usando `replacer` e `reviver`):


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

// Serialização personalizada
const jsonData = JSON.stringify(data, (key, value) => {
  if (typeof value === 'bigint') {
    return value.toString();
  } else {
    return value;
  }
});

console.log(jsonData);

// Desserialização personalizada
const parsedData = JSON.parse(jsonData, (key, value) => {
    if (typeof value === 'string' && /^[0-9]+$/.test(value)) { //verifica se é um número e uma string
      try {
        return BigInt(value);
      } catch(e) {
          return value;
      }
    }
    return value;
});

console.log(parsedData.id);

Compatibilidade com Navegadores

O BigInt é amplamente suportado nos navegadores modernos. No entanto, é essencial verificar a compatibilidade para navegadores ou ambientes mais antigos. Você pode usar uma ferramenta como Can I use para verificar o suporte do navegador. Se precisar suportar navegadores mais antigos, pode considerar o uso de um polyfill, mas esteja ciente de que os polyfills podem impactar o desempenho.

Considerações de Desempenho

Embora o BigInt forneça uma maneira poderosa de trabalhar com inteiros grandes, é importante estar ciente das possíveis implicações de desempenho.

Portanto, use o BigInt apenas quando necessário e otimize seu código para desempenho se estiver realizando um grande número de operações com BigInt.

Conclusão

O BigInt é uma adição valiosa ao JavaScript, permitindo que os desenvolvedores lidem com aritmética de inteiros grandes com precisão. Ao entender seus recursos, limitações e casos de uso, você pode aproveitar o BigInt para construir aplicações robustas e precisas em vários domínios, incluindo criptografia, cálculos financeiros e computação científica. Lembre-se de considerar a compatibilidade do navegador e as implicações de desempenho ao usar o BigInt em seus projetos.

Para Explorar Mais

Este guia fornece uma visão abrangente do BigInt em JavaScript. Explore os recursos vinculados para obter informações mais detalhadas e técnicas avançadas.