EA869 Representação Numérica Faculdade de Engenharia Elétrica e de Computação (FEEC) Universidade Estadual de Campinas (UNICAMP) Prof. Levy Boccato 1 Resumo Computabilidade Complexidade Problema Como organizar os dados? Como descrever um algoritmo? Algoritmo Nível conceitual Estruturas de dados Linguagens de programação Linguagem Assembly 2 Introdução Toda informação manipulada por um computador digital, seja um número correspondente ao valor de uma medida do ambiente, seja uma sequência de letras correspondente a uma mensagem, é representada internamente como sequências de bits (binary digits). A representação na base 2 se tornou a opção dominante devido aos avanços da semicondutores. eletrônica baseada em dispositivos Neste tópico, abordaremos a questão de como o computador representa e manipula números. Uma vez vistos estes conceitos, estaremos prontos para iniciar o estudo da arquitetura de computadores, começando pelo processador. 3 Introdução Sinal digital: Nível de tensão cujo valor exato não é relevante para o circuito, apenas a faixa à qual ele pertence. Nível alto (VH) – Bit 1 Região proibida Nível baixo (VL) – Bit 0 4 Introdução Problema: estamos habituados a representar números na base decimal. Porém, o computador lida com grandezas representadas como sequências de bits (base 2). Fazer manipulações com números binários pode ser custoso e pouco intuitivo para nós. Por isso, é necessário saber como converter números binários em outras bases mais familiares. Duas bases são de particular interesse: Decimal: por motivos óbvios. Hexadecimal: facilita a representação de sequências maiores de bits (útil, por exemplo, quando estamos trabalhando com arquiteturas de 16 ou 32 bits). 11111111111111112 6553510 FFFF16 5 Conversão de base Conversão de base: A transformação de um número representado em uma base genérica para a base decimal é feita segundo a expressão: n10 = di . bi + . . . . + d1 . b1 + d0 onde n10 é o número na base 10 correspondente à sequência de i+1 dígitos di na base b. Exemplos: A4 = 10×161 + 4×160 = 16410 00110111 = 1×25 + 1×24 + 1×22 + 1×21 + 1×20 = 5510 Binário para hexadecimal: 0001 1101 0110 1110 1 D 6 E 6 Números inteiros A conversão de base que vimos nos leva a interpretar a sequência de bits como sendo a representação de um número natural. Como podemos representar números inteiros (i.e., com sinal)? Só valores positivos 3 3 bits → 2 → 8 números (Naturais) Sinal e Magnitude 0 0 0 0 0 0 0 1 1 1 0 1 0 2 2 0 1 1 3 3 1 0 0 4 0 1 0 1 5 -1 1 1 0 6 -2 1 1 1 7 -3 Bit mais significativo expressa o sinal: 0 – positivo 1 - negativo Qual a vantagem e a desvantagem desta representação? Vantagem Relação natural com o uso comum Desvantagem - Dois circuitos (soma e subtração); - Duas representações para o zero; 7 Números inteiros A fim de contornar a necessidade de se ter dois circuitos aritméticos – um para a adição, outro para subtração –, trabalha-se com a representação por complemento. Neste contexto, a subtração entre dois números equivale à soma do primeiro com o complemento do segundo. Complemento de 1: Regra: o complemento de x (n bits, positivo) é dado por xc = 2n – 1 – x. Exemplo: Qual o complemento de 1 do número 1? Maior número representável 111 - 001 110 8 Números inteiros Complemento de 1: Usando 3 bits, teríamos os seguintes números representados. 0 0 0 0 0 0 1 1 0 1 0 2 0 1 1 3 1 0 0 -3 1 0 1 -2 1 1 0 -1 1 1 1 0 Qual o problema dessa representação? Redundância do zero Para obter diretamente a cadeia binária do complemento, basta inverter todos os bits. O bit mais significativo serve para indicar o sinal (exceto do zero). 9 Números inteiros Complemento de 2: Elimina o problema da redundância da representação do número 0, possibilitando a representação de um valor a mais. Regra: o complemento de x (n bits, positivo) é dado por xc = 2n – x. Exemplo: Qual o complemento de 2 do número 3? 1000 -3 = 8 – 3 = 5 - 011 101 10 Números inteiros Complemento de 2: 0 0 0 0 0 0 1 1 0 1 0 2 0 1 1 3 1 0 0 -4 1 0 1 -3 1 1 0 -2 1 1 1 -1 Positivos representáveis com n bits: 0 a 2n-1 - 1 Negativos representáveis com n bits: -1 a –2n-1 Para obter diretamente a cadeia binária do complemento de 2, basta inverter todos os bits e somar um ao valor. O bit mais significativo também para indicar o sinal (exceto do zero). 11 Números reais Como representar números reais no computador? Exemplos: 0,33 2,71828 (e) 3.847.992.023,45 Problema: pelo argumento diagonal de Cantor, sabemos que a cardinalidade do conjunto dos números reais ultrapassa a dos naturais. Logo, não é possível computar todos os números reais. Consequência: o que se processa em computador necessariamente é uma aproximação do conjunto dos números reais. Duas questões, portanto, são bastante importantes na escolha de uma representação para esta classe de números: Qual a faixa de valores representáveis? Qual a precisão que se atinge com a representação adotada? 12 Números reais Como representar números reais no computador? Primeira abordagem: representar um número real como um número natural. Ideia: a parte fracionária (após o ponto decimal) é representada por um número fixo de dígitos binários – quanto maior o número de bits, maior a precisão desta representação. 13 Números reais Ponto fixo Esquema de conversão: Multiplicação do valor real por 2d, onde d é o número de bits da parte fracionária. Arredondamento para o valor inteiro mais próximo. Exemplo: representação de 3,14 como um número inteiro de 8 bits com 4 bits para a parte fracionária. 3,14 × 24 = 50,24 ≅ 50 = 0011 0010 Pesos 0 0 1 1 0 0 1 0 23 22 21 20 2-1 2-2 2-3 2-4 8 4 2 1 0,5 0,25 0,125 , 0,0625 14 Ponto Fixo Operações com representação em ponto fixo Adição: equivalente à soma de inteiros Exemplo: 3,14 + 2,71 = 5,85 3,14 – representado como 0011 0010 = 50 2,71 – representado como 0010 1011 = 43 50 + 43 = 93 – 0101 1101 A cadeia binária 0101 1101 corresponde a 0 2 3 1 2 2 0 2 1 1 2 0 1 2 1 1 2 2 0 2 3 1 2 4 5,8125 Multiplicação: equivalente à multiplicação de inteiros com subsequente deslocamento para a direita do resultado conforme o número de bits que compõem a parte fracionária. Exemplo: 3,14 × 2,71 = 8,5094 50 × 43 = 2150 = 1000 0110 0110 Deslocamento para a direita de 4 bits: 1000 0110 1 2 3 0 2 2 0 21 0 2 0 0 2 1 1 2 2 1 2 3 0 2 4 8,375 15 Ponto Flutuante A representação em ponto flutuante é o padrão adotado pelos sistemas computacionais para expressar um número real. Notação científica: Um único dígito à esquerda da vírgula (ponto decimal ou binário). Normalização: o único dígito antes da vírgula sempre é diferente de zero. Exemplos: 4,0 × 10-9 1,011 × 2-8 0,1011 × 2-7 (Normalizado) (Normalizado) (Não-normalizado) 16 Ponto Flutuante Vantagens da notação científica normalizada: Simplifica a troca de dados que incluem números representados em ponto flutuante. Simplifica os algoritmos para aritmética de ponto flutuante. Aumenta a precisão dos números que podem ser armazenados em uma palavra. 0’s desnecessários são substituídos por dígitos verdadeiros à direita da vírgula. 17 Ponto Flutuante Representação (normalizada): 1,xxxxxx2 × 2yyyy k bits Sinal m bits yyyy 1,xxxxxx2 Expoente Fração, Mantissa ou Magnitude O primeiro bit indica o sinal do número. O campo seguinte contém os bits referentes ao expoente (positivo ou negativo), cujo tamanho representáveis. afeta decisivamente a faixa de números O último campo, denominado fração (mantissa ou magnitude), contém os dígitos válidos na forma normalizada. O tamanho deste campo determina a precisão da representação. Geral: (-1)S × F × 2E 18 Ponto Flutuante A representação em ponto flutuante está sujeita à ocorrência de overflow: Quando o expoente se torna excessivamente grande, não podendo ser representado no campo correspondente, temos um overflow. Há, também, a possibilidade de outro evento excepcional, denominado underflow: Quando o valor calculado se torna tão pequeno que não pode ser representado, temos um underflow. Ou, equivalentemente, um underflow ocorre quando o expoente negativo para a representação do número se torna muito grande e não cabe no campo correspondente. 19 Ponto Flutuante Solução de compromisso: Aumentar o número de bits destinados ao expoente amplia a faixa de números que podem ser representados. Além disso, reduz a possibilidade de ocorrência de overflow / underflow. Aumentar o número de bits destinados à mantissa melhora a precisão ou a capacidade representação adotada. de aproximação associada à Limitação: tamanho da palavra (memória / registrador). 20 Ponto Flutuante Padrão IEEE 754 (1985): Formato simples (single): 8 bits 31 30 29 28 S 27 23 bits 26 25 24 23 22 21 20 19 18 17 16 15 14 Expoente 13 12 11 10 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 Fração Formato duplo (double): 52 bits 11 bits 31 30 29 28 27 26 S 31 25 24 23 22 21 20 19 18 17 16 15 14 13 12 Expoente 30 29 28 27 26 25 24 11 10 Fração 23 22 21 20 19 18 17 16 15 14 13 12 11 10 Fração (continuação) 21 Ponto Flutuante Padrão IEEE 754 (1985): Bit escondido: dado que o bit mais significativo guardado na mantissa sempre seria igual a 1 – por causa da normalização –, ele não precisa ser armazenado junto com os demais bits, i.e., ele pode ser implícito à representação. Deste modo, no formato single, teremos efetivamente 24 bits de mantissa (1 implícito e 23 armazenados). No caso double, serão 53 bits. 22 Ponto Flutuante Padrão IEEE 754 (1985): Para facilitar a comparação entre dois números representados em ponto flutuante, o expoente é armazenado antes da mantissa. Contudo, expoentes negativos complicam um pouco a comparação, pois à primeira vista se parecem com valores de maior magnitude (sem sinal) – lembrar que o bit mais significativo é igual a 1. Desejável: maior expoente positivo = 11111 menor expoente negativo = 00000 Esta convenção nos leva à notação polarizada, na qual um bias é subtraído do expoente (sem sinal) armazenado para determinar o valor real deste campo. Valor escolhido: bias = 127 (single) bias = 1023 (double) 23 Ponto Flutuante Padrão IEEE 754 (1985): Com estas modificações, a representação em ponto flutuante segundo o padrão IEEE 754 é expressa da seguinte forma: (-1)S × (1 + mantissa) × 2(expoente – bias) Expandindo em função dos bits da mantissa b0, b1, b2, : (-1)S × (1 +(b1 × 2-1) + (b2 × 2-2) + (b3 × 2-3) + ) × 2(expoente – bias) Ou, equivalentemente: (-1)S × (1 +((b1 × 222) + (b2 × 221) + + (b23 × 20)) × 2-23) × 2(expoente – bias) 24 Ponto Flutuante Padrão IEEE 754 (1985): Números positivos: Menor expoente armazenado: 1 0 1,000... × 2-126 Maior expoente armazenado: 254 1,111... × 2-126 1,000... × 2127 1,111... × 2127 Precisão: 2-23× 2-126 Precisão: 2-23× 2127 Números negativos: -1,111... × 2127 -1,000... × 2127 -1,111... × 2-126 -1,000... × 2-126 Precisão: 2-23× 2127 0 Precisão: 2-23× 2-126 25 Ponto Flutuante Padrão IEEE 754 (1985): Representação do zero: Menor expoente: 000...0 Menor fração ou mantissa: 000...0 Símbolos especiais: O maior expoente (255) é reservado para símbolos especiais. Por exemplo, em vez de uma divisão por zero causar uma exceção, um símbolo +∞ ou -∞ pode ser retornado. Neste caso, a representação adotada é: expoente = 255 e fração = 000...0 Operações inválidas como 0 / 0 ou uma subtração entre dois valores infinitos produzem um símbolo NaN (not a number). A representação adotada para esta condição é: expoente = 255 e fração = valor não-nulo. 26 Ponto Flutuante Padrão IEEE 754 (1985): Em vez de haver um vazio (gap) entre o 0 e o menor valor normalizado, o padrão IEEE 754 aceita os chamados números não-normalizados. Características: Expoente armazenado é o mesmo que o utilizado para o valor zero (00...0). Fração não-nula. Correspondência: (-1)S × (0 + fração) × 2-126 Exemplo: Menor valor não-normalizado (single): 0,000...001 × 2-126 = 2-149. Maior valor não-normalizado (single): 0,111...111 × 2-126 = (1 – 2-23) × 2-126. 27 Ponto Flutuante Exemplo: k=4em=2 Neste caso, o bias é igual a 7. Expoente = 0 – reservado para o 0 e para os números não-normalizados. 1,00 × 2-6 = 0,01562500 1,01 × 2-6 = 0,01953125 1,10 × 2-6 = 0,02343750 1,11 × 2-6 = 0,02734375 1,00 × 27 = 128 1,01 × 27 = 160 1,10 × 27 = 192 1,11 × 27 = 224 Menor expoente válido Maior expoente válido Expoente = 15 – reservado para ±∞ e NaN. 28 Ponto Flutuante Padrão IEEE 754 (1985): Quadro resumo: 29 Ponto Flutuante Padrão IEEE 754 (1985): Exemplo: decimal para ponto flutuante (1) (2) (3) (4) (5) (6) 0,625 – base decimal 0,1012 – base 2 Normalizando: 1,01 × 2-1 Sinal: 0 Expoente: -1 + 127 = 126 Fração: 01000...0 0 0 1 1 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 S Expoente (7) Fração Palavra armazenada: 0x3f20 0000 30 Ponto Flutuante Padrão IEEE 754 (1985): Exemplo: ponto flutuante para decimal 1 1 0 0 0 0 0 1 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 S Expoente (1) (2) (3) (4) (5) Fração Palavra armazenada: 0xc1b2 4000 Sinal = 1 – número negativo Expoente = 131 – removendo a polarização, obtemos o expoente verdadeiro: 131 – 127 = 4. Fração = 2-2 + 2-3 + 2-6 + 2-9 Valor: (-1)S × (1 + fração) × 2(expoente – bias) (-1) × (1 + 0,392578125) × 24 = - 22,28125 31 Ponto Flutuante Adição: Passo 1: alinhar o ponto binário (decimal) tendo como referência o número com maior expoente. Para isto, basta deslocar o significando do menor número para a direita até que seu expoente corrigido se torne equivalente à referência. Passo 2: efetuar a operação de adição. Passo 3: normalizar o resultado. Passo 4: arredondar (truncar) para que valor obtido possua exatamente o número de dígitos permitido. Passo 5: caso necessário, efetuar nova normalização. 32 Ponto Flutuante Adição: 33 Ponto Flutuante Adição: Exemplo: 0,5 – 0,4375 Usando 4 bits para a representação: 0,5 = 0,1 × 20 = 1,000 × 2-1 -0,4375 = - 0,0111 × 20 = -1,110 × 2-2 O significando do número com menor expoente (no caso, -1,110) é deslocado para a direita até que seu expoente atinja o valor -1. -1,110 × 2-2 = -0,111 × 2-1 Soma: 1,000 × 2-1 – 0,111 × 2-1 = 0,001 × 2-1 34 Ponto Flutuante Adição: Exemplo: 0,5 – 0,4375 Normalize a soma, verificando a ocorrência de overflow ou underflow. 0,001 × 2-1 = 1,000 × 2-4 Arrendonde o resultado (neste caso em particular, não há necessidade de mudança). 1,000 × 2-4 = 0,0625. 35 Ponto Flutuante Multiplicação: Passo 1: calcular o expoente. Basta somar os expoentes armazenados!? E subtrair a polarização. Passo 2: multiplicar os significandos. Passo 3: normalizar o resultado. Passo 4: arredondar (truncar) para que valor obtido possua exatamente o número de dígitos permitido. Se necessário, efetuar nova normalização. Passo 5: determinar o sinal do resultado. 36 Ponto Flutuante Multiplicação: 37 Ponto Flutuante Multiplicação: Exemplo: 0,5 × (-0,4375) Usando 4 bits para a representação: 0,5 = 0,1 × 20 = 1,000 × 2-1 -0,4375 = - 0,0111 × 20 = -1,110 × 2-2 Somando os expoentes sem bias: -1 + (-2) = -3 Multiplicando os significandos: 1,000 × 1,110 = 1,110000 Podemos manter apenas 4 bits – 1,110 × 2-3 38 Ponto Flutuante Multiplicação: Exemplo: 0,5 × (-0,4375) O produto já está normalizado e não houve overflow / underflow. Não há necessidade de arredondar. Sinal: como os sinais dos operandos eram diferentes, o resultado será negativo. -1,110 × 2-3 Convertendo para decimal: -1,110 × 2-3 = -0,21875 39 Ponto Flutuante Bits de guarda e arredondamento: Para arredondar os números de maneira mais precisa, o hardware pode incluir bits extras nos cálculos. O padrão IEEE 574 prevê o uso de dois bits extras à direita durante as somas intermediárias, denominados bits de guarda e arredondamento. Exemplo: 2,56 × 100 + 2,34 × 102 Com bits extras: 2,3400 + 0,0256 = 2,3656 × 102 Arredondamento: 2,37 × 102 Sem bits extras: 2,34 + 0,02 = 2,36 × 102 40 Observações Alguns erros comuns: Deslocamento para a direita sempre implementa a divisão por uma potência de 2. Isso é verdade desde que seja um número sem sinal. Adição em ponto flutuante é associativa! Por causa da precisão limitada, é possível que arredondamentos intermediários levem a resultados diferentes dependendo da ordem em que as somas são feitas. b = 1,5 × 1038 c=1 Exemplo: a = -1,5 × 1038 (a + b) + c = 1 a + (b + c) = 0 41 Caracteres Vários dados de entrada e saída dos computadores não são números, vide o teclado, mouse, impressora etc. É muito comum nos depararmos com caracteres: Letras: A, B, …, Z Dígitos: 0, 1, …, 9 Caracteres especiais: ?, @, # Caracteres não-imprimíveis: ENTER, espaço A representação numérica que vimos até agora faz sentido para o hardware e para o programador de linguagem de baixo nível. Porém, o usuário final lida diretamente com caracteres. Como o computador associa estas duas representações? Byte Caracteres visíveis CÓDIGOS ALFANUMÉRICOS 42 Caracteres Existem diversos padrões de códigos alfanuméricos, sendo o de maior destaque o chamado código ASCII (American Standard Code for Information Interchange). ASCII: associa uma sequência de 8 bits (byte) a um caractere. Por exemplo, ao digitar OBA no teclado, o processador recebe a seguinte sequência de bits: OBA 1001111 1000010 1000001 43 Números + Programa Armazenado Importante: uma sequência de bits armazenada na memória não possui um significado intrínseco; isto é, ela pode ser interpretada de maneiras diferentes: Número sem sinal; Número inteiro; Número em representação de ponto flutuante; Caractere (ASCII); Instrução de máquina. A ação que o processador está executando no momento determina como aquela cadeia binária deve ser interpretada. Importante: há uma diferença fundamental entre os números representados em computador e os números do mundo real. Magnitude limitada. Precisão limitada. Podemos representar apenas 2N números, onde N é o número de bits para armazenamento/representação. 44 Créditos Este material está baseado nas notas de aula elaboradas pelo Prof. Léo Pini e pelo aluno de doutorado Tiago Novaes. 45