Em JavaScript, todos os números são representados internamente como ponto flutuante de 64 bits (formato IEEE 754), o que inclui tanto inteiros quanto decimais. Isso traz flexibilidade, mas também algumas armadilhas que podem causar resultados inesperados. Nesta aula, vamos explorar como os números funcionam, os métodos disponíveis no objeto Number, as funções matemáticas do objeto Math e a introdução ao BigInt para números inteiros de precisão arbitrária.

Entender essas nuances é fundamental para evitar bugs comuns, como problemas de precisão em cálculos financeiros ou comparações. Vamos começar.

Ponto flutuante e armadilhas

JavaScript usa o padrão IEEE 754 para representar números. Isso significa que números decimais são armazenados em binário, e alguns decimais simples (como 0.1) não podem ser representados exatamente. Por exemplo:

console.log(0.1 + 0.2); // 0.30000000000000004

Isso ocorre porque 0.1 e 0.2 em binário são dízimas periódicas. Portanto, operações com decimais podem resultar em pequenos erros de arredondamento. Para evitar problemas, é comum trabalhar com centavos (inteiros) em operações financeiras ou usar funções como toFixed para exibição.

Outra armadilha é a comparação direta de números de ponto flutuante. Nunca use === para comparar decimais após operações. Em vez disso, use uma margem de tolerância (epsilon):

const EPSILON = 1e-10;
console.log(Math.abs(0.1 + 0.2 - 0.3) < EPSILON); // true

Além disso, valores especiais como NaN (Not-a-Number) e Infinity podem surgir. NaN não é igual a si mesmo, então para verificar use Number.isNaN().

Number e métodos

O objeto Number fornece constantes e métodos úteis. Constantes incluem Number.MAX_VALUE, Number.MIN_VALUE, Number.POSITIVE_INFINITY, Number.NaN, e Number.EPSILON (a menor diferença entre dois números representáveis).

Métodos estáticos importantes:

  • Number.isInteger(val): retorna true se val é um inteiro.
  • Number.isFinite(val): verifica se val é finito (não Infinity nem NaN).
  • Number.isNaN(val): verifica se val é NaN (mais confiável que global isNaN).
  • Number.parseInt(str, base) e Number.parseFloat(str): convertem strings para números.

Métodos de instância (em números primitivos):

  • toFixed(digits): retorna string com número fixo de casas decimais.
  • toPrecision(digits): retorna string com precisão especificada.
  • toString(base): retorna string na base (2 a 36).

Exemplo:

const num = 123.456;
console.log(num.toFixed(2)); // "123.46"
console.log(num.toString(16)); // "7b.74bc6a7ef9db"
console.log(Number.isInteger(num)); // false

Objeto Math

O objeto Math contém constantes e funções matemáticas. Constantes: Math.PI, Math.E, Math.SQRT2, etc. Funções:

  • Math.abs(x): valor absoluto.
  • Math.ceil(x), Math.floor(x), Math.round(x): arredondamento.
  • Math.trunc(x): parte inteira (remove decimais).
  • Math.pow(base, exp), Math.sqrt(x), Math.cbrt(x).
  • Math.min(a, b, ...), Math.max(a, b, ...).
  • Math.random(): número pseudoaleatório entre 0 (inclusivo) e 1 (exclusivo).
  • Math.sin(x), Math.cos(x), Math.tan(x) (ângulos em radianos).
  • Math.log(x), Math.log2(x), Math.log10(x).

Exemplo de geração de número aleatório em um intervalo:

function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(randomInt(1, 10)); // número entre 1 e 10

BigInt (introdução)

BigInt é um tipo de dado que permite representar inteiros de qualquer tamanho, sem limitação de 64 bits. É criado adicionando n ao final de um número ou usando o construtor BigInt():

const big = 123456789012345678901234567890n;
const another = BigInt("123456789012345678901234567890");
console.log(big + 1n); // 123456789012345678901234567891n

BigInt não pode ser misturado com Number em operações aritméticas (lança TypeError). Deve-se converter explicitamente (com perda de precisão possível). Exemplo:

const n = 10n;
// console.log(n + 5); // TypeError
console.log(n + BigInt(5)); // 15n

Comparações entre BigInt e Number são permitidas, mas com cuidado:

console.log(10n == 10); // true (igualdade abstrata)
console.log(10n === 10); // false (estrita)

BigInt é útil para criptografia, cálculos com grandes números ou quando a precisão de 64 bits é insuficiente.

Boas práticas

  • Para cálculos financeiros, trabalhe com centavos (inteiros) ou use bibliotecas como decimal.js.
  • Evite comparar decimais diretamente; use tolerância ou converta para inteiros.
  • Use Number.isNaN em vez de isNaN global.
  • Prefira Math.trunc para extrair parte inteira (ao contrário de parseInt que pode interpretar strings).
  • Para números muito grandes, considere BigInt.

Referências

Exercícios

  1. Explique por que 0.1 + 0.2 !== 0.3 em JavaScript. Como você verificaria se a soma é aproximadamente igual a 0.3?

    ✓ Resposta: Isso ocorre porque 0.1 e 0.2 não podem ser representados exatamente em binário (IEEE 754), resultando em um pequeno erro de arredondamento. Para verificar se a soma é aproximadamente 0.3, podemos usar uma margem de tolerância: Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON.
  2. Escreva uma função que receba um número e retorne uma string com duas casas decimais, mas sem arredondamento (truncando). Exemplo: truncTwo(123.456) deve retornar "123.45".

    ✓ Resposta:
    function truncTwo(num) {
      return Math.trunc(num * 100) / 100 + '';
    }
    console.log(truncTwo(123.456)); // "123.45"
    
  3. Use o objeto Math para gerar um número aleatório entre 5 e 15 (inclusive ambos). Escreva o código.

    ✓ Resposta:
    const random = Math.floor(Math.random() * (15 - 5 + 1)) + 5;
    console.log(random); // inteiro entre 5 e 15
    
  4. Qual a diferença entre Number.isNaN() e a função global isNaN()? Dê um exemplo onde elas diferem.

    ✓ Resposta: isNaN() converte o argumento para número antes de verificar, enquanto Number.isNaN() não. Exemplo: isNaN("abc") retorna true (porque "abc" é convertido para NaN), mas Number.isNaN("abc") retorna false (pois a string não é NaN).
  5. Crie um BigInt a partir do número 2^53 (9007199254740992) e some 1 a ele. Mostre o resultado e explique por que isso é problemático com Number.

    ✓ Resposta:
    const big = 9007199254740992n;
    console.log(big + 1n); // 9007199254740993n
    
    Com Number, 9007199254740992 + 1 resulta em 9007199254740992 (pois é o limite de precisão inteira). BigInt permite representar exatamente números maiores.