| | | | |

1.2 Representação de Números em Máquina

Ajude a manter o site livre, gratuito e sem propagandas. Colabore!

Usualmente, números são manipulados em máquina através de suas representações em registros com n-bits. Ao longo desta seção, vamos usar a seguinte notação

[b1b2b3bn], (1.30)

para representar um registro de n-bits bi{0,1}, i=1,2,,n.

Na sequência, fazemos uma breve discussão sobre as formas comumente usadas para a manipulação de números em computadores.

1.2.1 Números Inteiros

O sistema de complemento de 2 é utilizado em computadores para a manipulação de números inteiros. Nesta representação, um registro de n bits

[d1d2d3dn], (1.31)

representa o número inteiro

x=(dn1d2d1)2dn2n1. (1.32)
Exemplo 1.2.1.

O registro de 8 bits22endnote: 2bits = 1 byte [B].

[1 1 0 0 0 0 0 0] (1.33)

representa o número

x =d8281+(d7d6d1)2 (1.34)
=027+(06050403021110)2 (1.35)
=21+20=3. (1.36)

Podemos implementar um conversor de registro para número inteiro como segue

Código 1: packbits8.py
1def packBitsInt8(dd):
2  x = -dd[7] * 2**7
3  for i, d in enumerate(dd[:7]):
4      x += d * 2**(i)
5  return x

Esta função, converte uma lista de bits (registro) no inteiro corresponde ao sistema de complemento 2.

1packBitsInt8([1,1,0,0,0,0,0,0])
3

Na representação de complemento de 2 com n bits, o menor e o maior números inteiros são obtidos com os registros

2n1[0 0 0 0 1], (1.37)
2n11[1 1 1 1 0], (1.38)

respectivamente. Já o zero é obtido com o registro

0[0 0 0 0 0 0 0 0]. (1.39)
Exemplo 1.2.2.

Com um registro de 8-bits, temos que o menor e o maior números inteiros que podem ser representados são

[0 0 0 0 0 0 0 1] (1.40)
27+(0000000)2=128, (1.41)

e

[1 1 1 1 1 1 1 0] (1.42)
027+(1111111)2=127, (1.43)

respectivamente.

Usando o Código 1, temos

1packBitsInt8([0,0,0,0,0,0,0,1])
-128
1packBitsInt8([1,1,1,1,1,1,1,0])
127
1packBitsInt8([0,0,0,0,0,0,0,0])
0
Observação 1.2.1.

No NumPy, o dtype=numpy.int8 corresponde a inteiros de 8 bits.

1import numpy as np
2np.array([-127, 0, 3, 128, 129], dtype=np.int8)
array([-127,    0,    3, -128, -127], dtype=int8)

Consulte a lista de tipos básicos do NumPy em NumPy:Data types.

A adição de números inteiros na representação de complemento de 2 pode ser feita de maneira simples. Por exemplo, consideremos a soma 3+9 usando registros de 8 bits. Temos

3 [1 1 0 0 0 0 0 0] (1.44)
9 [1 0 0 1 0 0 0 0]+ (1.45)
(1.46)
12 [0 0 1 1 0 0 0 0] (1.47)

No sistema de complemento de 2, a representação de um número negativo x pode ser obtida da representação de x, invertendo seus bits e somando 1. Por exemplo, a representação de 3 pode ser obtida da representação de 3, como segue

3[1 1 0 0 0 0 0 0]. (1.48)

Invertendo seus bits e somando 1, obtemos

3[1 0 1 1 1 1 1 1]. (1.49)

A subtração de números inteiros usando a representação de complemento de 2 fica, então, tanto simples quanto a adição. Por exemplo:

3 [1 1 0 0 0 0 0 0] (1.50)
9 [1 1 1 0 1 1 1 1]+ (1.51)
(1.52)
6 [0 1 0 1 1 1 1 1] (1.53)

1.2.2 Ponto Flutuante

A manipulação de números decimais em computadores é comumente realizada usando a representação de ponto flutuante de 64 bits33endnote: 3Padrão IEEE 754.. Nesta, um dado registro de 64 bits

[s|c10c9c0|×m1m2m52] (1.54)

representa o número

x=(1)sM2c1023, (1.55)

onde M é chamada de mantissa e c da característica, as quais são definidas por

M :=(1,m1m2m3m52)2, (1.56)
c :=(c10c2c1c0)2. (1.57)
Exemplo 1.2.3.

Por exemplo, na representação em ponto flutuante de 64 bits, temos que o registro

[1| 1 0 0 0|× 1 0 1 0 0 0] (1.58)

representa o número 3.25.

A seguinte função faz a conversão uma lista de 64 bits no número decimal corresponde ao sistema de ponto flutuante de 64 bits.

Código 2: packBitsDouble.py
1def packBitsDouble(ld):
2  s = ld[0]
3  c = 0
4  for i, d in enumerate(ld[1:12]):
5      c += d * 2**(10-i)
6  m = 1.
7  for i, d in enumerate(ld[12:]):
8      m += d * 2**(-(i+1))
9  x = m * 2**(c - 1023)
10  return -x if s else x

Por exemplo, usando-a para o registro acima, obtemos

1ld = [0]*14
2ld[0]=1
3ld[1]=1
4ld[12]=1
5ld[13]=1
6packBitsDouble(ld)
-3.5

1.2.3 Erro de Arredondamento

Dado um número real x, sua representação fl(x) em ponto flutuante é o registro que representa o número mais próximo de x. Este procedimento é chamado de arredondamento por proximidade.

A seguinte função obtém a representação em ponto flutuante de 64 bits de um dado número x44endnote: 4Esta função não é precisa e pode fornecer registros errados devido a erros de arredondamento. Uma alternativa melhor é apresentada na Observação 1.2.2..

Código 3: unpackBitsDouble.py
1import numpy as np
2
3def unpackBitsDouble(x):
4  ld = 64*[0]
5  if ( x == 0):
6      return ld
7  elif (x < 0):
8      ld [0] = 1
9  x = np.fabs(x)
10  c = int(np.log2(x) + 1023)
11  m = x/2**(c - 1023)
12  for i in range(11):
13      ld [11 - i] = c % 2
14      c //= 2
15  m -= 1
16  for i in range(52):
17      m *= 2
18      ld [12+ i] = int(m)
19      m %= 1
20  return ld

Por exemplo, x=1.1 é representado pelo registro

1ld = unpackBitsDouble(1.1)
2ld
[0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1, 0, 1, 0]

que corresponde ao número

1flx = packBitsDouble(ld)
2print(f'{flx:1.51f}')
1.10000000000000008881784197
0012523233890533447265625

O erro de arredondamento é |xfl(x)|8.9×1017.

Observação 1.2.2.

O seguinte código é uma solução mais pythonica para obter-se o registro em ponto flutuante de 64 bits de x=1.1.

1''.join(f'{c:08b}' for c in struct.pack('!d', 1.1))
’0011111111110001
1001100110011001
1001100110011001
1001100110011010’

Recomendamos consultar [4] para mais informações sobre a conversão eficiente de números decimais em pontos flutuantes.

Observemos que o erro de arredondamento varia conforme o número dado, podendo ser zero no caso de x=fl(x). Comumente, utiliza-se o épsilon de máquina como uma aproximação desse erro. O épsilon de máquina é definido como a distância entre o número 1 e seu primeiro sucessor em ponto flutuante. Temos

1ld = unpackBitsDouble(1)
2ld
[0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0]
1ld[63] = 1
2x = packBitsDouble(ld)
3x-1
2.220446049250313e-16

Ou seja, o épsilon de máquina é

eps:=2522.22×1016. (1.59)
Observação 1.2.3.

O método numpy.finfo pode ser usado para obtermos várias informações sobre o sistema de números em ponto flutuante. Por exemplo, temos

1import numpy as np
2finfo = np.finfo(np.double)
3finfo.eps
2.220446049250313e-16
1finfo.min
-1.7976931348623157e+308
1finfo.max
1.7976931348623157e+308

A aritmética em ponto flutuante requer arredondamentos sucessivos de números. Por exemplo, a computação da soma de dois números dados x e y é feita a partir de suas representações em ponto flutuante fl(x) e fl(y). Então, computa-se z=fl(x)+fl(y) e o resultado é fl(z). Observe, inclusive que fl(x+y) pode ser diferente de fl(fl(x)+fl(y)). Por exemplo

10.1 + 0.2 == 0.3
False

1.2.4 Exercícios Resolvidos

ER 1.2.1.

No sistema de complemento 2 de 8 bits, forneça o registro que representa os seguintes números inteiros:

  1. a)

    1

  2. b)

    -1

  3. c)

    15

  4. d)

    -15

Solução.

A seguinte função, obtém o registro de complemento 2 de 8-bits de um dado número inteiro x.

1def unpackBitsInt8(x):
2  ld = 8*[0]
3  if (x < 0):
4      ld[7] = 1
5      x += 2**(7)
6  for i in range(7):
7      ld[i] = x % 2
8      x //= 2
9  return ld

Usando-a, obtemos os seguintes resultados:

1# a) 1
2unpackBitsInt8(1)
[1, 0, 0, 0, 0, 0, 0, 0]
1# b) -1
2unpackBitsInt8(-1)
[1, 1, 1, 1, 1, 1, 1, 1]
1# c) 15
2unpackBitsInt8(15)
[1, 1, 1, 1, 0, 0, 0, 0]
1unpackBitsInt8(-15)
[1, 0, 0, 0, 1, 1, 1, 1]
ER 1.2.2.

Qual é o número decimal positivo mais próximo de zero que pode ser representado como um ponto flutuante de 64-bits. Também, forneça seu registro.

Solução.

Um registro em ponto flutuante de 64-bits tem a forma

[s|c10c9c0|×m1m2m52] (1.60)

e representa o número

x=(1)sM2c1023, (1.61)

onde M é chamada de mantissa e c da característica, as quais são definidas por

M :=(1,m1m2m3m52)2, (1.62)
c :=(c10c2c1c0)2. (1.63)

Tendo em vista que o registro nulo é reservado para o número decimal zero, temos que o número positivo mais próximo de zero é obtido com sinal s=0, a mantissa M=1 e a característica c=1, no que obtemos o decimal

x =21022 (1.64)
2.2250738585072014e308 (1.65)

Seu registro é

[0| 0 0 1| 0 0 0] (1.66)

O resultado pode ser verificado com os seguintes comandos:

1import numpy as np
2import struct
3x = np.finfo(np.double).tiny; x
2.2250738585072014e-308
1''.join(f'{c:08b}' \
2  for c in struct.pack('!d', x))
’0000000000010000
0000000000000000
0000000000000000
0000000000000000’
ER 1.2.3.

Em aplicações que não necessitam de muita precisão, a representação de números decimais no sistema de ponto flutuante de 32 bits é mais eficiente (no sentido de velocidade de processamento computacional). Neste sistema, um registro de 32-bits

[s|c7c6c0|×m1m2m23] (1.67)

representa o número

x=(1)sM2c127 (1.68)

onde,

M=(1,m1m2m23)2 (1.69)
c=(c7c6c0)2 (1.70)
  1. a)

    Forneça o registro do ponto flutuante de 32-bits que representa o número 42.5.

  2. b)

    Qual é o sucessor em ponto flutuante de 32-bits do número decimal 1. Forneça, também, o épsilon de máquina deste sistema.

Solução.
  1. a)

    O registro do ponto flutuante de 32-bits que representa o número 42.5 pode ser computado com o seguinte código:

    1x = 42.5
    2ld = 32*[0]
    3c = int(np.log2(x) + 127)
    4m = x/2**(c-127)
    5for i in range(8):
    6  ld[8-i] = c % 2
    7  c //= 2
    8m -= 1
    9for i in range(23):
    10  m *= 2
    11  ld[9+i] = int(m)
    12  m %= 1
    13ld
    [0, 1, 0, 0, 0, 0, 1, 0,
    0, 0, 1, 0, 1, 0, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0]
    

    Alternativamente, pode-se obter o registro como segue:

    1''.join(f'{c:08b}' \
    2for c in struct.pack('!f', 42.5))
    ’0100001000101010
    0000000000000000’
    
  2. b)

    No sistema de ponto flutuante de 32-bits, o sucessor de 1 tem o registro

    [0| 0 1 1 1|× 0 0 0 1] (1.71)

    donde, sua mantissa é m=1+223, característica c=127 e corresponde ao número decimal

    x=(1)0(1+223)2127127 (1.72)
    x=1+223 (1.73)

    Portanto, o épsilon de máquina neste sistema é

    eps =x1 (1.74)
    =223 (1.75)
    1np.float32(2**-23)
    1.1920929e-07
    

1.2.5 Exercícios

E. 1.2.1.

Considerando a representação de complemento de 2 de números inteiros, obtenha os registros de 8-bits dos seguintes números:

  1. a)

    17

  2. b)

    17

  3. c)

    32

  4. d)

    32

Resposta.

a) [10001000]; b) [11110111]
c) [00000100]; d) [00000111]

E. 1.2.2.

Considerando a representação de complemento de 2 de números inteiros, obtenha os registros de 16-bits dos seguintes números:

  1. a)

    1024

  2. b)

    1024

Resposta.

a) [0000000000100000];
b) [0000000000111111];

E. 1.2.3.

Considerando a representação de complemento de 2 de números inteiros, qual é o maior número que pode ser representado por um registro de 32-bits da forma

[1 0b2b3b4b30 1], (1.76)

onde bi{0,1}, i=2,3,4,,30.

Resposta.

[1011111]3

E. 1.2.4.

Obtenha os registros em ponto flutuante de 64-bits dos seguintes números:

  1. a)

    1.25

  2. b)

    3

Resposta.

a) [1| 0 1 1 1| 1 0 1 0 0 0];
b) [0| 1 0 0 0| 1 0 0 0]

E. 1.2.5.

Assumindo o sistema de ponto flutuante de 32-bits, obtenha o registro e o erro de arredondamento na representação dos seguintes números decimais:

  1. a)

    0.1

  2. b)

    10.1

  3. c)

    100.1

Resposta.
  1. a)
      [0, 0, 1, 1, 1, 1, 0, 1,
      1, 1, 0, 0, 1, 1, 0, 0,
      1, 1, 0, 0, 1, 1, 0, 0,
      1, 1, 0, 0, 1, 1, 0, 1]
    

    |0.1fl(0.1)|1.5e9

  2. b)
    [0, 1, 0, 0, 0, 0, 0, 1,
    0, 0, 1, 0, 0, 0, 0, 1,
    1, 0, 0, 1, 1, 0, 0, 1,
    1, 0, 0, 1, 1, 0, 1, 0]
    

    |10.1fl(10.1)|3.8e7

  3. c)
    [0, 1, 0, 0, 0, 0, 1, 0,
    1, 1, 0, 0, 1, 0, 0, 0,
    0, 0, 1, 1, 0, 0, 1, 1,
    0, 0, 1, 1, 0, 0, 1, 1]
    

    |100.1fl(100.1)|1.5e6


Envie seu comentário

As informações preenchidas são enviadas por e-mail para o desenvolvedor do site e tratadas de forma privada. Consulte a Política de Uso de Dados para mais informações. Aproveito para agradecer a todas/os que de forma assídua ou esporádica contribuem enviando correções, sugestões e críticas!