Contente:
Imagem: http://pikabu.ru/
Introdução
Logo nos primeiros dias de aprendizado de Java, me deparei com um tipo curioso de primitivos como os números de ponto flutuante. Fiquei imediatamente interessado em suas características e, mais ainda, na forma como foram escritos em código binário (que está interligado). Ao contrário de qualquer intervalo de números inteiros, mesmo em um intervalo muito pequeno (por exemplo, de 1 a 2) existe um número infinito deles. E tendo um tamanho de memória finito, é impossível expressar esse conjunto. Então, como eles são expressos em binário e como funcionam? Infelizmente, as explicações no
wiki e um artigo bastante interessante sobre Habré
aqui não me deram uma compreensão completa, embora tenham lançado as bases. A constatação só veio depois de ler este
artigo de análise na manhã seguinte à leitura.
Excursão pela história
(
retirado deste artigo sobre Habré ) Nos anos 60-70, quando os computadores eram grandes e os programas pequenos, ainda não existia um padrão único para cálculos, bem como um padrão para expressar o próprio número de ponto flutuante. Cada computador fazia isso de maneira diferente e cada um tinha seus próprios erros. Mas em meados dos anos 70, a Intel decidiu fabricar novos processadores com aritmética “melhorada” suportada e ao mesmo tempo padronizá-la. Os professores William Kahan e John Palmer (não, não são autores de livros sobre cerveja) foram contratados para desenvolvê-lo. Houve algum drama, mas um novo padrão foi desenvolvido. Agora este padrão é chamado IEEE754
Formato de número de ponto flutuante
Mesmo nos livros escolares, todos se deparavam com uma forma incomum de escrever números muito grandes ou muito pequenos na forma
1,2 × 10 3 ou
1,2 E3 , que é igual a
1,2 × 1000 = 1200 . Isso é chamado de método de notação exponencial. Neste caso, estamos lidando com a expressão de um número usando a fórmula:
N=M×n p , onde
- N = 1200 - o número resultante
- M = 1,2 - mantissa - parte fracionária, sem levar em conta ordens
- n = 10 é a base do pedido. Neste caso e quando não falamos de computadores, a base é o número 10
- p = 3 - grau de base
Muitas vezes, assume-se que a base da ordem é 10
e apenas a mantissa e o valor da base são escritos, separando-os com a letra
E. Em nosso exemplo, dei entradas equivalentes a
1,2 × 10 3 e
1,2 E3 . Se tudo estiver claro e terminamos a nostálgica excursão ao currículo escolar, agora recomendo esquecer isso, porque ao formar um número de ponto flutuante estamos lidando com potências de dois, não de dezenas, ou seja,
n = 2 , toda a fórmula harmoniosa
1.2E3 se desfaz e realmente quebrou meu cérebro.
Sinal e grau
O que temos então? Como resultado, temos também um número binário, que consiste em
uma mantissa - a parte que elevaremos a uma potência e a própria potência. Além disso, assim como é comum com os tipos inteiros, os números de ponto flutuante possuem um bit que determina o sinal – se o número será positivo ou negativo. Como exemplo, proponho considerar o tipo
float
, que consiste em 32 bits. Com números de precisão dupla
double
a lógica é a mesma, só que há o dobro de bits. Dos 32 bits, o primeiro mais significativo é alocado ao sinal, os próximos 8 bits são alocados ao expoente - a potência à qual aumentaremos a mantissa, e os 23 bits restantes - à mantissa. Para demonstrar, vejamos um exemplo:
A primeira parte é muito simples. Se o valor do primeiro bit
for 0 , o número obtido será
positivo . Se o bit for
1 , o número será
negativo . O próximo bloco de 8 bits é um bloco expoente. O expoente é escrito como um número comum
de oito bits e, para obter o grau necessário, precisamos subtrair do número resultante
127. No nosso caso, os oito bits do expoente são
10000001 . Isso corresponde ao número
129 . Se você tiver alguma dúvida sobre como calcular isso, a imagem mostra uma resposta rápida. Uma versão expandida pode ser obtida em qualquer curso de álgebra booleana.
1×2 7 + 0×2 6 + 0×2 5 + 0×2 4 + 0×2 3 + 0×2 2 + 0×2 1 + 1×2 0 = 1×128 + 1×1 = 128+ 1=129 Não é difícil calcular que o número máximo que podemos obter desses 8 bits é
11111111 2 = 255 10 (os subscritos
2 e
10 significam sistemas numéricos binários e decimais). No entanto, se usarmos apenas valores de expoentes positivos (
de 0 a 255 ), então os números resultantes terão muitos números antes da vírgula, mas não depois? Para obter valores negativos do grau, é necessário subtrair
127 do expoente gerado . Assim, a faixa de graus será
de -127 a 128 . Usando nosso exemplo, o grau exigido será
129-127 = 2 . Vamos lembrar esse número por enquanto.
Mantissa
Agora sobre a mantissa. É composto por 23 bits, mas no início há sempre outra unidade implícita, para a qual os bits não são alocados. Isso é feito por razões de conveniência e economia. O mesmo número pode ser expresso em diferentes potências adicionando zeros à mantissa antes ou depois da vírgula. A maneira mais fácil de entender isso é com um expoente decimal:
120.000 = 1,2×10 5 = 0,12×10 6 = 0,012×10 7 = 0,0012×10 8 etc. Porém, ao inserir um número fixo no cabeçalho da mantissa, receberemos novos números a cada vez. Vamos presumir que antes dos nossos 23 bits haverá mais um com um. Normalmente esse bit é separado dos demais por um ponto, o que, entretanto, não significa nada. É apenas mais conveniente 1. 1110000000000000000000
Agora a mantissa resultante precisa ser elevada a uma potência da esquerda para a direita, diminuindo a potência em um a cada passo. Partimos do valor da potência que obtivemos como resultado do cálculo, ou seja,
2 (escolhi deliberadamente um exemplo simples para não escrever cada valor da potência de dois e não os calculei na tabela acima quando o o bit correspondente é zero)
1×2 2 + 1×2 1 + 1×2 0 + 1×2 -1 = 1×4 + 1×2 + 1×1 + 1×0,5 = 4+2+1+0,5 = 7.5 e obtive o resultado
7.5 , a correção pode ser verificada, por exemplo,
neste link
Resultados
Um número de ponto flutuante padrão
float
consiste em 32 bits, o primeiro bit é o sinal (+ ou -), os próximos oito são o expoente, os próximos 23 são a mantissa. Por sinal - se o bit 0 for um número positivo. Se o bit 1 for negativo.
Por exponencial - convertemos bit a bit em um número decimal (o primeiro bit da esquerda é
128 , o segundo é
64 , o terceiro é
32 , o quarto é
16 , o quinto é
8 , o sexto é
4 , o sétimo é
2 , o oitavo é
1 ), subtraia
127 do número resultante , obtemos o grau com o qual começaremos.
De acordo com a mantissa - aos 23 bits existentes na frente adicionamos outro bit com o valor 1 e a partir dele começamos a aumentar até a potência que recebemos, diminuindo essa potência a cada bit subsequente.
Isso é tudo pessoal, crianças! PS: Como lição de casa, usando este artigo, deixe nos comentários suas versões do porquê ocorrem erros de precisão com um grande número de operações aritméticas com números de ponto flutuante
GO TO FULL VERSION