11/10/12 Página de Evy Salcedo Física Computacional ­ FSC­5705 Súmula Aulas Lista Conc. Freq. Outros Ensino Representação de um número pelo computador Converter decimal para binário Suponhamos que queremos converter o numero 100 para binário. O procedimento é simples, basta ir dividindo por 2 e os restos dessas divisões dão os bits do número em binário. Como se mostra na figura ao lado, o processo é relativamente simples. Dividimos o número, no caso 100 (dividendo), por 2 (divisor), o resto será o bit menos significativo 0 (representado a potencia 2 ). O quociente ou resultada, que no caso é 50, será o novo dividendo, o qual será divido por 2, o resto dessa operação será o segundo bit menos 1 significativo (representado a potencia 2 ). Esse processo continua até que o último quociente resulte em zero. Para escrever o número devemos escrever seguindo o sentido indicado pela zeta, do fim para o inicio, dessa forma o número que foi obtido em nossa conversão é 1100100. O último passo é colocar o número na base da maquina, por exemplo o número 1100100 é um número escrito com 7 bits, um computador não pode representar ele pois o tamanho mínimo é o byte que são 8 bits e representam um char, assim para passar para 8 bits simplesmente colocamos um zero a mais e dessa forma obtemos: 01100100. Note que se usamos um unsigned long int para guardar o nosso número ele será escrito como: 00000000000000000000000001100100. Utilizando o programa deste link podemos converter qualquer número para binário ou hexadecimal. Você pode perceber que os dados binários são escritos ao contrario. Isso se deve a que em computadores de tipo Intel (arquitetura x86) o padrão dos números é o chamado de "little endian", diferentemente do outro padrão chamado de "big endian" (a transmissão de dados através rede - internet - é nesse formato). O little endian se carateriza por colocar o byte menos significativo no inicio da memoria e o byte mais significativo no fim da memoria. Como mostrado na figura embaixo, o big endian coloca o byte mais significativo na posição de memoria mais baixa e o menos significativo. Representação de inteiros com sinal Até agora analisamos como é representado um número inteiro dentro da memoria do computador. /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula111.html 1/6 11/10/12 Página de Evy Salcedo Para se definir como representar os números negativos, foi escolhida a chamada representação complemento de dois. Utilizando essa representação para se obter o negativo do número desejado temos que seguir o seguintes passos Calcule o complemento boolenao de cada bit (ou seja se é zero passa a ser um e viceversa) Some 1 ao sinal Além disso se convencionou que o bit mais significativo será zero se o número for positivo, caso ele seja negativo o número será negativo. Para entender isso melhor vejamos um exemplo, vamos converter o número +18 → 00010010 complemento bit a bit → 11101101 somando 1 em binario 18 para negativo: 00000001 −18 → 11101110 Existe uma expressão matemática que permite converter um dado número binário, levando em consideração a representação de complemento dois, para a base decimal: n−2 n−1 A = −2 i a n−1 + ∑ 2 a i i=0 Assim, no caso de nosso exemplo: 7 00010010 → −2 11101110 → −2 100000000 → −2 7 7 6 ∗ 0 + 2 6 ∗ 1 + 2 6 ∗ 0 + 2 5 ∗ 0 + 2 5 ∗ 1 + 2 5 ∗ 0 + 2 4 ∗ 0 + 2 4 ∗ 1 + 2 4 ∗ 0 + 2 3 ∗ 1 + 2 3 ∗ 0 + 2 3 ∗ 0 + 2 2 ∗ 0 + 2 2 ∗ 1 + 2 2 ∗ 0 + 2 1 ∗ 0 + 2 1 ∗ 1 + 2 1 ∗ 0 + 2 0 ∗ 1 + 2 0 ∗ 1 + 2 0 ∗ 0 + 2 ∗ 0 → 18 ∗ 1 → −18 ∗ 1 → 0 Na última linha (primeira coluna) é colocado o resultado da soma dos 18 + (−18) na representação booleana, e como é de se esperar o resultado foi zero. Observe que sobrou um um a mais na posição 9, esse número é desconsiderado (chamado de carry). /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula111.html 2/6 11/10/12 Página de Evy Salcedo Na figura acima utilizamos o programa convertDecimalToBinaryAndHex.c para ver a representação booleana de 18 e −18. Observamos que os resultados coincidem com nossos cálculos. Esta explicação permite entender o porque os números com sinal som menos de que aqueles com sinal, em geral se temos n bits para representar um número, a faixa de números decimais n−1 n−1 que podemos representar está dada por −2 → +2 − 1, o menos um se deve a que o zero é um número positivo, nessa representação, e portanto consome uma possível representação. Para finalizar é importante mencionar que está não é a única representação possível para os números negativos, mas é a representação que apresenta vantagens consideráveis: O zero tem uma representação única (ficando fácil testar quando um número é zero) e as operações matemática básicas (soma e resta) ficam simples, simplesmente se lembrando que 0 + 0 = 0, 1 + 0 = 0 + 1 = 1 e 1 + 1 = 10 (observe o 1 que é o carry - vai um). Representação de ponto Flutuante Para representar um número de ponto flutuante os computadores utilizam a notação científica ou seja, os números são representados segundo o formato Com isso em mente os desenhadores da Intel construíram a primeira unidade de ponto flutuante (FPU) no microprocessador 8086 para lidar com 3 tipos de números de ponto flutuante diferente, os números de precisão simples (float) - de 32 bits ou 4 bytes, os números de precisão dupla (double) - de 64 bits ou 8 bytes, e os números de precisão estendida de 80 bits ou 10 bytes. O trabalho desenvolvidos por eles (Kahn, Coonan, and Stone) foi tão bom que serviu como base para o padrão definido pelo IEEE (I-3-E: Institute of Electrical and Electronics Engineers) no standart 754. /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula111.html 3/6 11/10/12 Página de Evy Salcedo Seguindo a notação científica, se instituiu que os números de precisão simples (float) terão o bit mais significativo para o sinal (±), os seguintes 8 bits para o expoente e os restantes 23 bits para o significado (ou mantissa, como era antigamente chamado). Os números de dupla precisão (double) terão o bit mais significativo para o sinal (±), os seguintes 11 bits para o expoente e os restantes 52 bits para o significado. Os números de precisão estendida terão o bit mais significativo para o sinal (±), os seguintes 11 bits para o expoente e os restantes 63 bits para o significado. Esta representação de números reais foi durante muitos anos o padrão utilizado pelos processadores da Intel (e as outras companhias que aderiram ao padrão x86 da Intel) para o processamento interno de operações de ponto flutuante, sem importar se o número era definido como float ou double. Esse cenário mudou em com a substitução da unidade FPU x87 (devido a que o coprocessador matemático que acompanhou o Intel 8086, chamado de Intel 8087) pela nova FPU SSE (Streaming SIMD Extensions) com desenho vetorial (No SSE ou "3DNow! Profissional", o processador ganha a capacidade de leer 128 bits de forma simultânea, ou 4 bytes, o que corresponde a 4 float. Com isso ele pode realizar operações matemáticas ou lógicas nesses dados e colocar de volta na memoria, ou seja, ele pode somar 2 pares de números "simultaneamente". Recentemente foi introduzido o AVX - Advanced Vector Extensions - estendendo para 256 bits essa capacidade, podendo ser possível somar 2 pares de doubles). Converter de real para binário ± a 1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 b 1 b 2 b 3 … b 23 Quando o número decimal é representado em formato binário podem ser utilizadas duas formas de representação, a forma normalizada ou a forma denormalizada. Na forma normalizada, que é a representação mais usada, o bit mais significativo é assumido ser sempre 1, dessa forma todo ±E número será escrito na forma ±1.b1 b2 b3 … b23 × 2 , onde os b são os 23 bits que são utilizados para representar a mantissa (ou significado, como é atualmente chamada a mantissa), E é o expoente definido no 8 bits (a 1 a 2 a 3 … a 8 ). Assim vemos que se assumimos que o bit mais significativo é 1, nossos 23 bit de significado na verdade representam 24 bit (já que o 1 é assumido). O expoente utiliza a representação polarizada. Para o caso de 8 bit no expoente podemos 8 representar 2 − 1 = 255 números e nesse caso a polarização é 255/2 = 127. Assim podemos representar números desde −127 até 128. Para entender melhor vejamos a tabela seguinte bits do expoente (a1 a2 a3 … a8 ) representação numérica −126 (00000000) = (0) ±(0.b 1 b 2 b 3 … b 23 ) ×2 (00000001) = (1) ±(1.b 1 b 2 b 3 … b 23 ) ×2 (00000010) = (2) ±(1.b 1 b 2 b 3 … b 23 ) ×2 (00000011) = (3) ±(1.b 1 b 2 b 3 … b 23 ) ×2 2 2 2 2 10 2 10 −126 2 10 −125 2 10 −124 2 ↓ ↓ 0 (01111111) = (127) ±(1.b 1 b 2 b 3 … b 23 ) ×2 (10000000) = (128) ±(1.b 1 b 2 b 3 … b 23 ) ×2 2 2 10 2 10 1 2 ↓ ↓ 125 (11111100) = (252) ±(1.b 1 b 2 b 3 … b 23 ) ×2 (11111101) = (253) ±(1.b 1 b 2 b 3 … b 23 ) ×2 (11111110) = (254) ±(1.b 1 b 2 b 3 … b 23 ) ×2 (11111111) = (255) 2 2 2 2 10 2 10 2 10 10 2 ±∞ se 126 127 , N aN em outros casos b 1 = … b 23 /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula111.html 4/6 11/10/12 Página de Evy Salcedo Além de sabermos como se expressa o expoente ±∞ se b1 = … b23 , temos que considerar os valores associado ao significado ou mantissa. Quando representamos os números inteiros vimos que a ordem dos bits aumentava de direita para esquerda, isto é o primeiro bit de esquerda para 0 1 2 direita deve ser multiplicado por 2 , o segundo por 2 , o terceiro por 2 , …, o nessimo de n 2 esquerda para direita deve ser multiplicado por , logo é somado cada um dos resultados e me é possível expressar o número binário em decimal. No caso em questão, onde queremos expressão um número real em binário devemos considerar que o bit esquerda, no campo do significado ou mantissa, continua a ser o bit mais significativo só que agora o valor atribuído a cada bit é diferente como se mostra na tabela embaixo b1 b2 b3 b4 1 1 2 1 = 1 2 1 2 1 = 1 4 1 2 3 = 1 8 2 4 = b2 3 … 1 16 1 … 2 23 = 1 8388608 Para entender melhor essa representação vejamos um exemplo, vamos representar o número 123.687510 em binário. Primeiro escrevemos a parte inteira do número real em binário, ou seja 123 = 1111011 e agora escrevemos a parte decimal da seguinte forma: dividimos a parte −1 −2 fracionaria por 2 = 0.5 , o resto dessa operação é divida por 2 = 0.25 e assim sucessivamente até obter zero como resto ou esgotar as 23 possibilidade ( −23 −7 2 = 1.1920928955078125 × 10 . Na figura acima é mostrado o procedimento passo a passo, para o exemplo acima, mas note que se o número for mudado para 123.687610 o número de bit (bi ) necessários para representar ele se torna muito grande (provavelmente infinito) ou seja, não é possível representar exatamente, como pode ser verificado utilizando o programa converteFracaoParaBinario.c. (Como teste baixe o programa e verá que ele imprime a diferença na representação e no número, teste para o caso em que o número for 0.1 armazenado em um −08 float, nesse caso a diferença é de aproximadamente 9.536743164062636 × 10 . Na verdade o padrão IEEE estabelece que o número deve ser arredondado já seja para cima ou para baixo). Continuando com o exemplo então a parte fracionaria é 0.687510 e representada em binário por pelo numero 0.1011, somando à representação da parte inteira do número obtemos que a representação de 123.687510 é 1111011.10112 , passando para notação "científica" (com base 2, é claro), o número binário pode ser escrito como: 6 1.1110111011 × 2 . Assim, no computador a mantissa do número será representada utilizando, a forma normalizada (que suprime o 1 inicial), como 11101110110000000000000 (para o caso de um float). O expoente foi 6 na representação polarizada é o número 6 + 127 = 13310 , que em binário é 10000101. Juntando tudo, e colocando o bit 31 igual a 0 pois o número é positivo, obtemos a representação binaria de 123.687510 em um float de 32 bits como: 0 10000101 11101110110000000000000 , no caso de um computador da família Intel esse número será representado internamente no formato little endian, assim obteremos 00000000 01100000 ; 11110111 01000010 (a fim de verificar utilize o programa convertDecimalToBinaryAndHex.c) O menor número positivo que podemos representar no formato normalizado é 0 00000001 0000000000000000000000 que equivale aproximadamente ao número −126 Nmin = (1.0000000000000000000000) × 2 2 −126 = 2 −38 = 1.1754943508222875 × 10 No outro extremos temos o máximo número positivo no formato normalizado que pode ser representado: 0 11111110 11111111111111111111111 que equivale aproximadamente ao número 127 Nmin = (1.11111111111111111111111) × 2 2 127 = 1.9999998807907104 × 2 38 = 3.4028234663852886 × 10 Infinitos e NaN Na última linha da representação dos números de ponto flutuante está a definição do padrão IEEE-745 para tratar alguns casos excepcionais que podem ocorrer exemplo 1/0 = ∞. Para tratar essa e outras exepções, inicialmente se consideraba atribuir a esse operação o máximo /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula111.html 5/6 11/10/12 Página de Evy Salcedo valor representável, o problema desta abordagem é que o usuario não fica sabendo que seu programa deu pau, de fato, se você aceita essa convenção então a operação 1/0 − 1/0 = 0 não fica bem representada. Uma outra alternativa seria simplesmente para o programa e avisar que se deu um erro de divisão por zero, mas isto também não ajudaria, como exemplo considere um programa que calcule a resistencia equivalente de 2 o mais resistores em paralelo, se algum dos resistores for zero aparecerá uma destas exepções, contudo como 1/Req = 1/R1 + 1/R2 , vemos que o processo não tem porque parar e sem continuar pois o resultado de R eq = 0 . Dessa forma o IEEE introduz um padrão para os bits que representam esse tipo de exepção. A vatagem desta avordagem é que essa representação pode ser testada de forma a tomar uma descição quando ela venha ocorrer. /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula111.html 6/6