A primeira vez a 0.1 + 0.2 === 0.3 bug me mordeu na produção, EU fiz o que a maioria dos desenvolvedores fazem: EU pesquisei, encontrei o & quot; ponto flutuante é estranho, & quot; e segui em frente Foi anos antes de EU realmente olhar para os bits e entender o porquê Uma vez que EU fiz, toda uma categoria de bugs numéricos deixou de ser misteriosa Este guia caminha através do que um conversor IEEE 754 mostra, usando o Conversor IEEE 754 no Toolz.dev, e por que olhar para o sinal, expoente e mantissa é a maneira mais rápida de construir uma intuição real sobre como os computadores armazenam decimais.
tl;dr: Os computadores armazenam números de ponto flutuante como um sinal, uma potência de dois e um significando fracionário, seguindo o padrão IEEE 754 Um conversor IEEE 754 codifica um decimal como 3.14159 nesses bits exatos e decodifica bits de volta para um valor Conversor IEEE 754 faz as duas coisas, para precisão simples e dupla, inteiramente no seu navegador, com o sinal, expoente e mantissa quebrados.
O que é o IEEE 754?
IEEE 754 é o padrão publicado pela primeira vez em 1985 e revisado como IEEE 754-2019, que define como os computadores representam números de ponto flutuante em binário Quase todas as CPUs, GPU e linguagem de programação seguem, e é por isso que a double comporta-se da mesma forma em C, Java, Python e JavaScript Quando as pessoas dizem & quot; ponto flutuante, & quot; quase sempre significam ponto flutuante binário IEEE 754.
A ideia central é que um número é armazenado em três partes Há um bit de sinal que diz positivo ou negativo Há um expoente que escala o valor por uma potência de dois E há uma mantissa, também chamada de fração ou significando, que detém os dígitos precisos Colocados juntos, um número normal é sinal vezes 1. mantissa vezes 2 para o expoente É notação científica, na base dois, embalado em um número fixo de bits.
Os dois formatos que você encontra diariamente são precisão única, chamada binary32, e precisão dupla, chamada binary64. single usa 1 bit de sinal, 8 bits de expoente e 23 bits de mantissa, para 32 bits no total Double usa 1 bit de sinal, 11 bits de expoente e 52 bits de mantissa, para 64 bits no total Double é o tipo de ponto flutuante padrão na maioria dos idiomas; single é comum em GPUs e em código sensível à memória.
O que um conversor IEEE 754 realmente faz?
Um conversor IEEE 754 pega um número e mostra seu padrão exato de bits, dividido nesses três campos, junto com a forma hexadecimal e o valor lido de volta dos bits. Funciona nos dois sentidos: digite um decimal para codificá-lo ou cole um padrão de bits binário ou hexadecimal para decodificá-lo novamente no número que representa.
No Toolz.dev o fluxo de trabalho é curto Escolha precisão simples ou dupla Escolha se sua entrada é um número decimal, um padrão de bits hexadecimais ou um padrão de bits binários Insira o valor A ferramenta mostra o bit de sinal, o campo de expoente, a mantissa, os bits completos, o hexadecimal, tanto o expoente tendencioso quanto imparcial, e o valor exato armazenado Todo campo tem um botão de cópia.
A razão para olhar para tudo isso, em vez de confiar no número que seu idioma imprime, é que o número impresso está por omissão Quando seu console mostra 0.1, ele está mostrando uma renderização arredondada e amigável Os bits dizem o que realmente é armazenado, e a diferença entre esses dois é onde os bugs de ponto flutuante vivem.
Por que 0,1 mais 0,2 não é igual a 0,3?
Este é o exemplo canônico, e o conversor o torna concreto O valor 0,1 não pode ser representado exatamente em ponto flutuante binário, pela mesma razão 1/3 não pode ser escrito exatamente em decimal Na base dez, 1/3 é 0,3333... para sempre Na base dois, 0,1 é uma fração repetida que nunca termina, então tem que ser arredondado para caber em 52 bits de mantissa.
Decode o duplo para 0,1 e o valor que os bits realmente possuem é 0,10000000000000055511151231257827021181583404541015625. O duplo para 0,2 é similarmente um cabelo muito grande Adicione os dois valores arredondados e o resultado é um pouquinho um pouco mais que 0,3, mas o duplo para 0,3 é um pouquinho menor, então os dois não são iguais Nada está quebrado Cada passo está fazendo exatamente o que o IEEE 754 exige, e o conversor permite que você veja isso acontecer em vez de levá-lo à fé.
Depois de ter visto isso, a correção segue naturalmente: nunca compare floats para igualdade exata Compare dentro de uma pequena tolerância, ou use centavos inteiros por dinheiro, ou uma biblioteca decimal quando você precisa de aritmética decimal exata O bug nunca esteve na adição Estava em esperar um formato binário para armazenar frações decimais exatamente.
Quais são o sinal, o expoente e a mantissa?
Cada campo tem um trabalho específico e o conversor rotula todos os três.
O bit de sinal é o mais simples: 0 significa positivo, 1 significa negativo. Como o sinal é um bit separado, o IEEE 754 tem um zero positivo e um negativo, que se comportam igualmente em comparações, mas carregam bits diferentes.
O campo expoente armazena uma potência de dois, mas com um viés adicionado para que possa representar expoentes negativos sem um sinal separado O viés é 127 para precisão simples e 1023 para duplo Então, um campo expoente armazenado de 127 em precisão única significa um expoente real de zero O conversor Toolz.dev mostra ambos os números: o valor tendencioso que vive nos bits, e a potência imparcial de dois que realmente se aplica Essa distinção faz tropeçar muitas pessoas, e é por isso que a ferramenta soletra em vez de fazer você subtrair em sua cabeça.
A mantissa detém a parte fracionária do significando Para um número normal há um 1 inicial implícito que não é armazenado, porque um significando binário normalizado sempre começa com 1, então o formato obtém um pouco de precisão livre deixando-o de fora É por isso que a precisão única dá cerca de 7 dígitos decimais e o dobro dá cerca de 15 a 16, mesmo que os campos de mantissa sejam 23 e 52 bits.
Veja como os dois formatos se alinham campo por campo:
| Propriedade | Solteiro (binário32) | Duplo (binário64) |
|---|---|---|
| Bits totais | 32 | 64 |
| Bocados de sinal | 1 | 1 |
| Bits expoentes | 8 | 11 |
| Bocados Mantissa | 23 | 52 |
| Viés do expoente | 127 | 1023 |
| Aprox. dígitos decimais | 7 | 15 a 16 |
| Nome comum | flutuar | duplo |
Como converto bits de volta para um número decimal?
A decodificação é tão útil quanto a codificação, e é como você lê um valor de um despejo de memória, um formato de arquivo binário ou um protocolo de rede que armazena flutuadores brutos Defina o tipo de entrada como Hex ou Binário e cole o padrão de bits exato que a precisão espera: 8 dígitos hexadecimais ou 32 bits para single, 16 dígitos hexadecimais ou 64 bits para double.
Por exemplo, o hexágono de precisão única 40490FDB decodifica para 3.1415927, o flutuador mais próximo de pi. O conversor também aceita um opcional 0x prefixo e ignora espaços e sublinhados, para que você possa colar bytes agrupados à medida que os encontra Se você der o número errado de dígitos, ele informa exatamente quantos essa precisão precisa em vez de truncar silenciosamente, que é o tipo de mensagem de erro que desejo que mais ferramentas se preocupem em escrever.
Esta direção de decodificação emparelha naturalmente com um geral conversor de base numérica quando você precisa mover um valor entre decimal, hex, binário e octal sem a interpretação de ponto flutuante, e com o tradutor binário quando você está trabalhando com texto em vez de números Eu alcancei todos os três com frequência suficiente para que eles vivam no mesmo canto dos meus marcadores.
Como o infinito, o NaN e o zero são representados?
Os valores especiais são onde o IEEE 754 fica inteligente e o conversor identifica cada caso para você, para que você não precise memorizar os padrões.
O campo expoente atua como um switch Quando cada bit expoente é 1, você está no intervalo especial: uma mantissa totalmente zero significa infinito, e qualquer mantissa diferente de zero significa NaN, não um número O infinito positivo e negativo diferem apenas pelo bit de sinal NaN é o que você obtém de operações como zero dividido por zero ou a raiz quadrada de um negativo, e tem a famosa propriedade de que ele não é igual a si mesmo.
Quando cada bit de expoente é 0, você está no outro intervalo especial: uma mantissa totalmente zero é zero, positiva ou negativa dependendo do bit de sinal, e uma mantissa diferente de zero é um número subnormal Os subnormais preenchem a lacuna entre zero e o menor número normal, trocando precisão pela capacidade de representar magnitudes muito minúsculas Eles usam um expoente efetivo de um menos o viés, e eles deixam cair o 1 inicial implícito, que o conversor contabiliza quando mostra o expoente imparcial.
Todo o resto, onde o campo expoente não é nem todos os zeros nem todos os uns, é um número normal Ser capaz de olhar para um padrão de bits e saber instantaneamente qual desses cinco casos você está olhando é uma superpotência de depuração genuína quando um cálculo produz um valor que você não esperava.
Quando é que preciso mesmo disto?
Você precisa dele com mais frequência do que você imagina uma vez que você sabe para olhar Eu alcancei quando um resultado numérico está desligado por um erro de arredondamento e EU quero confirmar se o valor é exatamente representável Eu uso ao escrever ou depurar um serializador binário, um formato de arquivo, ou um protocolo de fio que armazena flutuadores, para que EU possa verificar os bytes correspondem ao que EU pretendia É indispensável ao comparar precisão simples e dupla, por exemplo, decidir se um shader GPU pode usar float sem perder muita precisão E é um dos materiais didáticos mais claros que conheço para arquitetura de computadores, porque ver os campos torna tangível o padrão abstrato.
Se você construir através da pilha da maneira que EU faço, ponto flutuante aparece em lugares surpreendentes: um cálculo de preço em uma API Laravel, uma animação de tela no React, uma exportação CSV que mangles uma coordenada Entender o formato de armazenamento é um pequeno investimento que compensa em todos eles Eu escrevi sobre como ferramentas como esta se encaixam em um kit mais amplo no Kit de ferramentas para desenvolvedores web, e o guia conversor base numérica e guia tradutor binário cubra as conversões vizinhas de baixo nível com mais profundidade.
Quais são os limites da precisão de ponto flutuante?
Uma vez que você pode ver os bits, os limites do formato deixam de ser abstratos Um duplo tem 52 bits mantissa armazenados mais o inicial implícito 1, que funciona para cerca de 15 a 16 dígitos decimais significativos Push passado que e precisão silenciosamente evapora O maior inteiro um duplo pode representar com cada valor no meio ainda exato é 2 para a 53 a potência, que é 9007199254740992. adicione um a isso e as rodadas de resultado de volta para baixo, porque o próximo valor representável é dois de distância, não um. É por isso que as linguagens que usam duplas para todos os números, JavaScript entre eles, expõem a Number.MAX_SAFE_INTEGER constante exatamente nesse valor e por que grandes IDs de banco de dados enviados como números JSON podem corromper silenciosamente.
A lacuna entre um valor representável e o próximo é chamada de unidade em último lugar, ou ULP, e cresce à medida que a magnitude cresce Perto de 1,0 a lacuna é minúscula; perto de um bilhão são algumas centenas; perto do duplo máximo, cerca de 1,8 vezes 10 elevado a 308, a lacuna entre vizinhos é astronomicamente grande O conversor torna isso visível: codifique dois números grandes próximos e muitas vezes você descobrirá que eles compartilham os mesmos bits, porque simplesmente não há representação entre eles Entendendo que o ULP é o que impede você de esperar mais resolução do que o formato pode dar.
Uma armadilha relacionada é o cancelamento catastrófico Quando você subtrai dois números de ponto flutuante quase iguais, os dígitos iniciais se cancelam e você fica com os bits de ordem inferior, que eram a parte menos precisa para começar O resultado pode ser dominado pelo erro de arredondamento, mesmo que cada entrada parecesse precisa É por isso que o código numericamente cuidadoso reordena as operações para evitar subtrair quantidades próximas, e por que somar uma longa lista de flutuadores em um loop ingênuo acumula erro que melhores algoritmos como a soma de Kahan evitam.
As conclusões práticas são consistentes Não armazene dinheiro como um float; use centavos inteiros ou um tipo decimal, porque um valor como 0,10 não é exatamente representável e os erros compostos em milhares de transações Não compare floats com igualdade exata; compare dentro de uma tolerância dimensionada para o seu problema E quando um inteiro muito grande deve sobreviver a uma viagem de ida e volta, mantenha-o como uma string ou use um tipo inteiro de precisão arbitrária em vez de confiar em um duplo para mantê-lo Cada uma dessas regras é mais fácil de lembrar depois que o conversor mostrou por que o formato se comporta da maneira que faz.
Perguntas frequentes
Como converto um número decimal para IEEE 754?
Escolha precisão simples ou dupla, mantenha o tipo de entrada em Decimal, digite seu número e converta A ferramenta codifica o valor em seu padrão de bits IEEE 754 e mostra o bit de sinal, campo de expoente, mantissa, binário completo e hexadecimal Tudo isso é executado em seu navegador.
Qual é a diferença entre precisão simples e dupla?
A precisão simples (binary32) usa 32 bits: 1 sinal, 8 expoente e 23 mantissa, dando cerca de 7 dígitos decimais de precisão A precisão dupla (binary64) usa 64 bits: 1 sinal, 11 expoente e 52 mantissa, dando cerca de 15 a 16 dígitos O dobro é o tipo float padrão na maioria dos idiomas; o single é comum em GPUs e em código com restrição de memória.
Por que 0.1 não converte para um valor exato?
0,1 não tem representação finita no ponto flutuante binário, assim como 1/3 não tem decimal finito O duplo representável mais próximo é 0,100000000000005511151231257827021181583404541015625. O conversor mostra o valor armazenado lido de volta dos bits, para que você possa ver a pequena diferença que causa surpresas de arredondamento como 0,1 + 0,2 não igualando 0,3.
Quais são o sinal, o expoente e a mantissa?
O bit de sinal é 0 para positivo e 1 para negativo O campo expoente armazena uma potência de dois com um viés adicionado (127 para simples, 1023 para duplo), razão pela qual a ferramenta mostra tanto o valor tendencioso nos bits quanto a potência imparcial que aplica A mantissa detém a parte fracionária do significando, com um implícito levando 1 para números normais.
Posso converter IEEE 754 bits de volta para um número decimal?
Sim. defina o tipo de entrada para Hex ou Binário e cole o padrão de bits exato: 8 dígitos hexadecimais ou 32 bits para precisão única, 16 dígitos hexadecimais ou 64 bits para duplo A ferramenta decodifica-o e mostra o valor decimal que representa junto com a quebra de campo completa.
Como o infinito, o NaN e o zero são representados?
Quando cada bit expoente é 1, uma mantissa totalmente zero significa infinito e uma mantissa diferente de zero significa NaN. Quando cada bit expoente é 0, uma mantissa totalmente zero é zero (positiva ou negativa dependendo do bit de sinal) e uma mantissa diferente de zero é um número subnormal O conversor rotula cada um desses casos para você.
É seguro usar esta ferramenta para números sensíveis?
Sim. a conversão é executada inteiramente em JavaScript no seu navegador Nenhuma solicitação de rede carrega sua entrada, nada é armazenado e a ferramenta funciona offline depois que a página é carregada, portanto, qualquer valor inserido permanece no seu dispositivo.
Que formato hexadecimal o conversor espera?
Dígitos hexadecimais simples, com um prefixo opcional de 0 x e espaços ou sublinhados para agrupamento, que são ignorados A precisão única espera 8 dígitos hexadecimais e a precisão dupla espera 16 Por exemplo, 40490 FDB é pi de precisão única (3.1415927).
Explore você mesmo os bits com o grátis Conversor IEEE 754. Ele codifica e decodifica flutuadores de precisão simples e duplos inteiramente em seu navegador, sem nada carregado.



