O tipo numérico primário do JavaScript, Número, é usado para representar inteiros e aproximar números reais. O JavaScript representa números usando o formato de ponto flutuante de 64 bits definido pelo padrão IEEE 754, o que significa que pode representar números tão grandes quanto ± 1,7976931348623157 × 10 e tão pequenos quanto ± 5 × 10.
O formato de número JavaScript permite que você represente exatamente todos os inteiros entre −9.007.199.254.740.992 (−2) e 9.007.199.254.740.992 (2), inclusive. Se você usar valores inteiros maiores do que isso, poderá perder a precisão dos dígitos finais. Observe, entretanto, que certas operações em JavaScript como indexação de array e os operadores bit a bit são realizadas com inteiros de 32 bits.
Quando um número aparece diretamente em um programa JavaScript, é chamado de literal numérico. O JavaScript oferece suporte a literais numéricos em vários formatos. Observe que qualquer literal numérico pode ser precedido por um sinal de menos (-) para tornar o número negativo.
Literais inteiros
Em um programa JavaScript, um inteiro de base 10 é escrito como uma sequência de dígitos. Por exemplo:
0
3
10000000
Além de literais inteiros de base 10, o JavaScript reconhece valores hexadecimais (base 16). Um literal hexadecimal começa com 0x ou 0X, seguido por uma sequência de dígitos hexadecimais. Um dígito hexadecimal é um dos dígitos de 0 a 9 ou as letras de a (ou A) a f (ou F), que representam os valores de 10 a 15. Aqui estão alguns exemplos de literais inteiros hexadecimais:
0xff // => 255: (15*16 + 15)
0xBADCAFE // => 195939070
No ES6 e posterior, você também pode expressar inteiros em binário (base 2) ou octal (base 8) usando os prefixos 0b e 0o (ou 0B e 0O) em vez de 0x:
0b10101 // => 21: (1*16 + 0*8 + 1*4 + 0*2 + 1*1)
0o377 // => 255: (3*64 + 7*8 + 7*1)
Literais de ponto flutuante
Literais de ponto flutuante podem ter um ponto decimal; eles usam a sintaxe tradicional para números reais. Um valor real é representado como a parte integral do número, seguido por um ponto decimal e a parte fracionária do número.
Literais de ponto flutuante também podem ser representados usando notação exponencial: um número real seguido pela letra e (ou E), seguido por um sinal opcional de mais ou menos, seguido por um expoente inteiro. Essa notação representa o número real multiplicado por 10 à potência do expoente.
Mais sucintamente, a sintaxe é:
[digitos][.digitos][(E|e)[(+|-)]digitos]
Por exemplo:
3.14
2345.6789
.333333333333333333
6.02e23 // 6.02 × 10²³
1.4738223E-32 // 1.4738223 × 10⁻³²
SEPARADORES EM LITERAIS NUMÉRICOS
Você pode usar sublinhados em literais numéricos para quebrar literais longos em partes que são mais fáceis de ler:
let billion = 1_000_000_000; // Sublinhado como um separador de milhares.
let bytes = 0x89_AB_CD_EF; // Como separador de bytes.
let bits = 0b0001_1101_0111; // Como um separador de bits.
let fraction = 0.123_456_789; // Funciona na parte fracionária também.
No momento da redação deste artigo, no início de 2020, os sublinhados em literais numéricos ainda não estavam formalmente padronizados como parte do JavaScript. Mas eles estão em estágios avançados do processo de padronização e são implementados por todos os principais navegadores e pelo Node.
Aritmética em JavaScript
Os programas JavaScript funcionam com números usando os operadores aritméticos que a linguagem oferece. Estes incluem + para adição, - para subtração, * para multiplicação, / para divisão e % para módulo (resto após a divisão). ES2016 adiciona ** para exponenciação.
Além desses operadores aritméticos básicos, JavaScript suporta operações matemáticas mais complexas por meio de um conjunto de funções e constantes definidas como propriedades do objeto Math:
Math.pow(2,53) // => 9007199254740992: 2 elevado à potência 53
Math.round(.6) // => 1.0: arredondar para o número inteiro mais próximo
Math.ceil(.6) // => 1.0: arredondado para um número inteiro
Math.floor(.6) // => 0.0: arredondar para um número inteiro
Math.abs(-5) // => 5: valor absoluto
Math.max(x,y,z) // Retorna o maior argumento
Math.min(x,y,z) // Retorna o menor argumento
Math.random() // Número pseudo-aleatório x onde 0 <= x <1,0
Math.PI // π: circunferência de um círculo / diâmetro
Math.E // e: A base do logaritmo natural
Math.sqrt(3) // => 3**0.5: a raiz quadrada de 3
Math.pow(3, 1/3) // => 3**(1/3): a raiz cúbica de 3
Math.sin(0) // Trigonometria: também Math.cos, Math.atan, etc.
Math.log(10) // Logaritmo natural de 10
Math.log(100)/Math.LN10 // Logaritmo de base 10 de 100
Math.log(512)/Math.LN2 // Logaritmo de base 2 de 512
Math.exp(3) // Math.E ao cubo
ES6 define mais funções no objeto Math:
Math.cbrt(27) // => 3: raiz cúbica
Math.hypot(3, 4) // => 5: raiz quadrada da soma dos quadrados de todos os argumentos
Math.log10(100) // => 2: Logaritmo de base 10
Math.log2(1024) // => 10: Logaritmo de base 2
Math.log1p(x) // Log natural de (1 + x); preciso para x muito pequeno
Math.expm1(x) // Math.exp(x)-1; o inverso de Math.log1p()
Math.sign(x) // -1, 0 ou 1 para argumentos <, == ou> 0
Math.imul(2,3) // => 6: multiplicação otimizada de inteiros de 32 bits
Math.clz32(0xf) // => 28: número de bits zero à esquerda em um inteiro de 32 bits
Math.trunc(3.9) // => 3: converte para um inteiro truncando a parte fracionária
Math.fround(x) // Arredondar para o número flutuante de 32 bits mais próximo
Math.sinh(x) // Seno hiperbólico. Também Math.cosh(), Math.tanh()
Math.asinh(x) // Arco hiperbólico. Também Math.cosh(), Math.tanh()
A aritmética em JavaScript não levanta erros em casos de estouro, estouro negativo ou divisão por zero. Quando o resultado de uma operação numérica é maior do que o maior número representável (estouro), o resultado é um valor infinito especial, Infinito. Da mesma forma, quando o valor absoluto de um valor negativo se torna maior do que o valor absoluto do maior número negativo representável, o resultado é infinito negativo, -Infinito. Os valores infinitos se comportam como você esperaria: somar, subtrair, multiplicar ou dividir por qualquer coisa resulta em um valor infinito (possivelmente com o sinal invertido).
O estouro negativo ocorre quando o resultado de uma operação numérica está mais próximo de zero do que o menor número representável. Nesse caso, o JavaScript retorna 0. Se ocorrer underflow de um número negativo, o JavaScript retorna um valor especial conhecido como “zero negativo”. Esse valor é quase completamente indistinguível do zero normal e os programadores de JavaScript raramente precisam detectá-lo.
A divisão por zero não é um erro em JavaScript: ela simplesmente retorna infinito ou infinito negativo. Porém, há uma exceção: zero dividido por zero não tem um valor bem definido e o resultado dessa operação é o valor especial não numérico, NaN. NaN também surge se você tentar dividir infinito por infinito, obter a raiz quadrada de um número negativo ou usar operadores aritméticos com operandos não numéricos que não podem ser convertidos em números.
O JavaScript predefine constantes globais Infinity e NaN para manter o valor infinito positivo e não um número, e esses valores também estão disponíveis como propriedades do objeto Number:
Infinity // Um número positivo muito grande para representar
Number.POSITIVE_INFINITY // Mesmo valor 1/0 // => Infinity
Number.MAX_VALUE * 2 // => Infinity; transbordar
-Infinity // Um número negativo muito grande para representar
Number.NEGATIVE_INFINITY // Mesmo valo
-1/0 // => -Infinity
-Number.MAX_VALUE * 2 // => -Infinity
NaN // O valor não numérico
Number.NaN // O mesmo valor, escrito de outra forma
0/0 // => NaN
Infinity/Infinity // => NaN
Number.MIN_VALUE/2 // => 0: underflow
-Number.MIN_VALUE/2 // => -0: negative zero
-1/Infinity // -> -0: also negative 0
-0
// As seguintes propriedades de número são definidas em ES6
Number.parseInt() // Igual à função parseInt() global
Number.parseFloat() // Igual à função parseFloat() global
Number.isNaN(x) // X é o valor NaN?
Number.isFinite(x) // X é um número finito?
Number.isInteger(x) // X é um inteiro?
Number.isSafeInteger(x) // É x um número inteiro - (2 ** 53) <x <2 ** 53?
Number.MIN_SAFE_INTEGER // => -(2**53 - 1)
Number.MAX_SAFE_INTEGER // => 2**53 - 1
Number.EPSILON // => 2**-52: menor diferença entre os números
O valor não numérico tem uma característica incomum em JavaScript: ele não se compara a nenhum outro valor, incluindo ele mesmo. Isso significa que você não pode escrever x === NaN para determinar se o valor de uma variável x é NaN. Em vez disso, você deve escrever x !== X ou Number.isNaN(x). Essas expressões serão verdadeiras se, e somente se, x tiver o mesmo valor que a constante global NaN.
A função global isNaN() é semelhante a Number.isNaN(). Ele retorna verdadeiro se seu argumento for NaN ou se esse argumento for um valor não numérico que não pode ser convertido em um número. A função relacionada Number.isFinite() retorna true se seu argumento for um número diferente de NaN, Infinity ou -Infinity. A função global isFinite() retorna true se seu argumento for, ou puder ser convertido em, um número finito.
O valor zero negativo também é um tanto incomum. Ele compara igual (mesmo usando o teste de igualdade estrita do JavaScript) a zero positivo, o que significa que os dois valores são quase indistinguíveis, exceto quando usados como um divisor:
let zero = 0; // Zero regular
let negz = -0; // Zero negativo
zero === negz // => true: zero e zero negativo são iguais
1/zero === 1/negz // => false: Infinity e -Infinity não são iguais
Erros binários de ponto flutuante e arredondamento
Existem infinitos números reais, mas apenas um número finito deles (18.437.736.874.454.810.627, para ser exato) pode ser representado exatamente pelo formato de ponto flutuante JavaScript. Isso significa que quando você está trabalhando com números reais em JavaScript, a representação do número geralmente será uma aproximação do número real.
A representação de ponto flutuante IEEE-754 usada pelo JavaScript (e quase todas as outras linguagens de programação modernas) é uma representação binária, que pode representar exatamente frações como 1/2, 1/8 e 1/1024. Infelizmente, as frações que usamos com mais freqüência (especialmente ao realizar cálculos financeiros) são frações decimais: 1/10, 1/100 e assim por diante. As representações de ponto flutuante binárias não podem representar exatamente números tão simples como 0,1.
Os números do JavaScript têm muita precisão e podem se aproximar de 0,1 muito aproximadamente. Mas o fato de que esse número não pode ser representado exatamente pode levar a problemas. Considere este código:
let x = .3 - .2; // trinta centavos menos 20 centavos
let y = .2 - .1; // vinte centavos menos 10 centavos
x === y // => false: os dois valores não são iguais!
x === .1 // => false: .3-.2 não é igual a .1
y === .1 // => true: .2-.1 é igual a .1
Por causa do erro de arredondamento, a diferença entre as aproximações de .3 e .2 não é exatamente a mesma que a diferença entre as aproximações de .2 e .1. É importante entender que esse problema não é específico do JavaScript: ele afeta qualquer linguagem de programação que usa números de ponto flutuante binários. Além disso, observe que os valores x e y no código mostrado aqui são muito próximos entre si e com o valor correto. Os valores calculados são adequados para quase todos os fins; o problema só surge quando tentamos comparar valores de igualdade.
Se essas aproximações de ponto flutuante forem problemáticas para seus programas, considere o uso de inteiros em escala. Por exemplo, você pode manipular valores monetários como centavos inteiros em vez de dólares fracionários.
Inteiros de precisão arbitrária com BigInt
Um dos mais novos recursos do JavaScript, definido no ES2020, é um novo tipo numérico conhecido como BigInt. No início de 2020, ele foi implementado no Chrome, Firefox, Edge e Node, e há uma implementação em andamento no Safari. Como o nome indica, BigInt é um tipo numérico cujos valores são inteiros. O tipo foi adicionado ao JavaScript principalmente para permitir a representação de inteiros de 64 bits, que são necessários para compatibilidade com muitas outras linguagens de programação e APIs. Mas os valores BigInt podem ter milhares ou até milhões de dígitos, caso você precise trabalhar com números tão grandes. (Observe, no entanto, que as implementações do BigInt não são adequadas para criptografia porque não tentam evitar ataques de temporização.)
Literais BigInt são escritos como uma string de dígitos seguidos por uma letra n minúscula. Por padrão, estão na base 10, mas você pode usar os prefixos 0b, 0o e 0x para BigInts binários, octais e hexadecimais:
1234n // Um literal BigInt não tão grande
0b111111n // Um BigInt binário
0o7777n // Um BigInt octal
0x8000000000000000n // => 2n**63n: Um inteiro de 64 bitsr
Você pode usar BigInt() como uma função para converter Números ou strings de JavaScript para valores BigInt:
BigInt(Number.MAX_SAFE_INTEGER) // => 9007199254740991n
let string = "1" + "0".repeat(100); // 1 seguido por 100 zeros.
BigInt(string) // => 10n**100n: um googol
A aritmética com valores BigInt funciona como a aritmética com números JavaScript regulares, exceto que a divisão elimina qualquer resto e arredonda para baixo (para zero):
1000n + 2000n // => 3000n
3000n - 2000n // => 1000n
2000n * 3000n // => 6000000n
3000n / 997n // => 3n: o quociente é 3
3000n % 997n // => 9n: e o restante é 9
(2n ** 131071n) - 1n // Um primo de Mersenne com 39.457 dígitos decimais
Embora os operadores padrão +, -, *, /,% e ** funcionem com BigInt, é importante entender que você não pode misturar operandos do tipo BigInt com operandos de número regulares. Isso pode parecer confuso no início, mas há um bom motivo para isso. Se um tipo numérico fosse mais geral do que o outro, seria fácil definir a aritmética em operandos mistos para simplesmente retornar um valor do tipo mais geral. Mas nenhum dos tipos é mais geral do que o outro: BigInt pode representar valores extraordinariamente grandes, tornando-o mais geral do que os números regulares. Mas BigInt só pode representar inteiros, tornando o tipo de número JavaScript regular mais geral. Não há como contornar esse problema, portanto, o JavaScript o contorna, simplesmente não permitindo operandos mistos para os operadores aritméticos.
Os operadores de comparação, por outro lado, funcionam com tipos numéricos mistos:
1 < 2n // => true
2 > 1n // => true
0 == 0n // => true
0 === 0n // => false: O === verifica a igualdade de tipo também
Os operadores bit a bit geralmente funcionam com operandos BigInt. Nenhuma das funções do objeto Math aceita operandos BigInt, entretanto.
Datas e horas
O JavaScript define uma classe Date simples para representar e manipular os números que representam datas e horas. As datas de JavaScript são objetos, mas também têm uma representação numérica como um carimbo de data/hora que especifica o número de milissegundos decorridos desde 1º de janeiro de 1970:
let timestamp = Date.now(); // A hora atual como um carimbo de data/hora (um número).
let now = new Date(); // A hora atual como um objeto Date.
let ms = now.getTime(); // Converte para um carimbo de data/hora em milissegundos.
let iso = now.toISOString(); // Converte para uma string no formato padrão.
Conteúdo retirado do livro "Javascript The Definitive Guide" de David Flanagan.