JSON Toolkit

Por que inteiros grandes perdem precisão

Resumo: Inteiros acima de 2^53-1 perdem precisão no JSON.parse. IDs snowflake, números de pedido e bigints de banco de dados todos esbarram nisso, então mantenha-os como strings de ponta a ponta.

Intermediário~4 min de leituraPublicado em 2026-10-30Atualizado em 2026-11-04
Por que inteiros grandes perdem precisão em JSON e como mantê-los

Resumo da ópera: trate IDs snowflake, números de pedido e chaves primárias bigint como strings durante serialização, transporte e análise; mantenha o tipo numérico apenas quando o campo for comprovadamente limitado a 2^53-1, e então imponha isso com uma restrição maximum no Schema.

Porque o dano acontece durante a análise, silenciosamente.

JSON.parse('{"id": 9007199254740993}');
// { id: 9007199254740992 }

O dígito que falta sumiu; não há nada a recuperar.

De onde vem o 2^53

O JavaScript tem um tipo numérico: precisão dupla IEEE 754, com 53 bits de significando (um implícito, 52 explícitos). Inteiros exatos, portanto, param em ±(2^53-1):

-9007199254740991 .. 9007199254740991

Acima dessa linha, valores representáveis consecutivos distam 2, 4, 8. 9007199254740993 é ímpar, e nessa magnitude todo valor representável é par, então ele arredonda para 9007199254740992.

Campos que são atingidos

Campo Dígitos Risco
IDs snowflake 18–19 Alto, os últimos dígitos mudam
Números de pedido / transação 16–20 Alto
Chaves primárias bigint de banco de dados 19 Alto
Dinheiro em unidades menores — Baixo
Timestamps em milissegundos 13 Seguro

O fio condutor: estes são identificadores, não quantidades. Um identificador é transportado, nunca somado nem multiplicado, então string é o tipo correto.

Quatro correções

Serialize como string. O backend converte campos bigint antes de escrever a resposta. Simples e durável; documente que esses campos são strings.

Deixe o serializador cuidar disso. O ToStringSerializer do Jackson para Long, a tag json:",string" do Go. Mesma ideia: mantenha o valor fora do caminho numérico.

Analise com suporte a BigInt. json-bigint e bibliotecas semelhantes. Observe que BigInt não pode se misturar com aritmética Number, e JSON.stringify o rejeita.

Restrinja a faixa. Se o campo realmente não pode passar de 2^53, adicione maximum no Schema para que dados fora da faixa falhem na validação em vez de falhar silenciosamente no navegador.

Como saber se você tem esse bug

Pegue um ID conhecido e faça a ida e volta: serialize, transmita, analise e compare caractere por caractere. Ou inspecione o corpo da resposta cru — um número de 19 dígitos sem aspas que chega com os últimos dígitos errados é quase certamente uma perda em tempo de análise.

Perguntas frequentes

Por que o JSON.parse muda os últimos dígitos sem erro?

O JavaScript tem um tipo numérico, precisão dupla IEEE 754, então inteiros acima de 2^53-1 só podem cair no valor representável mais próximo. JSON.parse trata isso como uma conversão numérica normal: não lança nem avisa.

Como o frontend consegue manter inteiros grandes intactos?

Use uma biblioteca como json-bigint, porque JSON.parse não consegue. O custo é que BigInt não pode se misturar com aritmética Number e JSON.stringify o rejeita, então o código ao redor também precisa mudar.

A documentação da API deve declarar que esses campos são strings?

Sim. Serializar para strings é a correção mais simples e durável, mas só funciona se os consumidores tratarem o valor como texto, então o tipo do campo precisa estar escrito no contrato da API.

Leitura adicional