Ponto flutuante decimal - Decimal floating point

A aritmética de ponto flutuante decimal ( DFP ) refere-se a uma representação e operações em números de ponto flutuante decimal . Trabalhar diretamente com frações decimais (base 10) pode evitar os erros de arredondamento que normalmente ocorrem ao converter entre frações decimais (comuns em dados inseridos por humanos, como medições ou informações financeiras) e frações binárias (base 2).

A vantagem da representação de ponto flutuante decimal sobre a representação de ponto fixo decimal e inteiro é que ela oferece suporte a uma faixa muito mais ampla de valores. Por exemplo, enquanto uma representação de ponto fixo que aloca 8 dígitos decimais e 2 casas decimais pode representar os números 123456,78, 8765,43, 123,00 e assim por diante, uma representação de ponto flutuante com 8 dígitos decimais também pode representar 1,2345678, 1234567,8, 0,000012345678, 12345678000000000 e assim por diante. Essa faixa mais ampla pode reduzir drasticamente o acúmulo de erros de arredondamento durante cálculos sucessivos; por exemplo, o algoritmo de soma de Kahan pode ser usado em ponto flutuante para adicionar muitos números sem acúmulo assintótico de erro de arredondamento.

Implementações

Usos início mecânicos de ponto flutuante decimal são evidentes no ábaco , régua de cálculo , a calculadora Smallwood , e algumas outras calculadoras que as entradas de apoio em notação científica . No caso das calculadoras mecânicas, o expoente é frequentemente tratado como informação secundária que é contabilizada separadamente.

O computador IBM 650 suportava um formato de ponto flutuante decimal de 8 dígitos em 1953. A máquina Wang VS, de outra forma binária, suportava um formato de ponto flutuante decimal de 64 bits em 1977. A biblioteca de suporte de ponto flutuante para o processador Motorola 68040 fornecia um 96 formato de armazenamento de ponto flutuante decimal de bits em 1990.

Algumas linguagens de computador têm implementações de aritmética de ponto flutuante decimal, incluindo PL / I , C # , Java com grande decimal, emacs com calc e módulo decimal do Python . Em 1987, o IEEE lançou o IEEE 854 , um padrão para computação com ponto flutuante decimal, que carecia de uma especificação de como os dados de ponto flutuante deveriam ser codificados para intercâmbio com outros sistemas. Isso foi posteriormente abordado no IEEE 754-2008 , que padronizou a codificação de dados de ponto flutuante decimal, embora com dois métodos alternativos diferentes.

IBM POWER6 e processadores POWER mais recentes incluem DFP no hardware, assim como o IBM System z9 (e máquinas zSeries posteriores). SilMinds oferece SilAx, um coprocessador DFP vetorial configurável . IEEE 754-2008 define isso com mais detalhes. A Fujitsu também possui processadores Sparc de 64 bits com DFP no hardware.

Microsoft C # ou .NET usa System.Decimal.

Codificação IEEE 754-2008

O padrão IEEE 754-2008 define representações de ponto flutuante decimal de 32, 64 e 128 bits. Como os formatos de ponto flutuante binários, o número é dividido em um sinal, um expoente e um significando . Ao contrário do ponto flutuante binário, os números não são necessariamente normalizados; valores com poucos dígitos significativos têm várias representações possíveis: 1 × 10 2 = 0,1 × 10 3 = 0,01 × 10 4 , etc. Quando o significando é zero, o expoente pode ser qualquer valor.

Formatos de ponto flutuante decimal IEEE 754-2008
decimal32 decimal64 decimal 128 decimal (32 k ) Formato
1 1 1 1 Campo de sinal (bits)
5 5 5 5 Campo de combinação (bits)
6 8 12 w = 2 × k + 4 Campo de continuação do expoente (bits)
20 50 110 t = 30 × k −10 Campo de continuação de coeficiente (bits)
32 64 128 32 × k Tamanho total (bits)
7 16 34 p = 3 × t / 10 + 1 = 9 × k −2 Tamanho do coeficiente (dígitos decimais)
192 768 12288 3 × 2 w = 48 × 4 k Faixa de expoente
96 384 6144 Emax = 3 × 2 w −1 O maior valor é 9,99 ... × 10 Emax
-95 -383 -6143 Emin = 1 − Emax O menor valor normalizado é 1,00 ... × 10 Emin
-101 -398 -6176 Etiny = 2 − p − Emax O menor valor diferente de zero é 1 × 10 Etiny

Os intervalos de expoentes foram escolhidos de forma que o intervalo disponível para valores normalizados seja aproximadamente simétrico. Como isso não pode ser feito exatamente com um número par de valores expoentes possíveis, o valor extra foi dado a Emáx.

Duas representações diferentes são definidas:

  • Um com um campo de significando inteiro binário codifica o significando como um grande número inteiro binário entre 0 e 10 p −1. Espera-se que seja mais conveniente para implementações de software usando uma ALU binária .
  • Outro com um campo de significando decimal densamente compactado codifica dígitos decimais mais diretamente. Isso torna a conversão de e para a forma de ponto flutuante binário mais rápida, mas requer hardware especializado para manipular com eficiência. Espera-se que seja mais conveniente para implementações de hardware.

Ambas as alternativas fornecem exatamente o mesmo intervalo de valores representáveis.

Os dois bits mais significativos do expoente são limitados ao intervalo de 0−2 e os 4 bits mais significativos do significando são limitados ao intervalo de 0−9. As 30 combinações possíveis são codificadas em um campo de 5 bits, junto com formas especiais para infinito e NaN .

Se os 4 bits mais significativos do significando estiverem entre 0 e 7, o valor codificado começará da seguinte maneira:

s 00mmm xxx   Exponent begins with 00, significand with 0mmm
s 01mmm xxx   Exponent begins with 01, significand with 0mmm
s 10mmm xxx   Exponent begins with 10, significand with 0mmm

Se os 4 bits iniciais do significando forem binários 1000 ou 1001 (decimal 8 ou 9), o número começa da seguinte maneira:

s 1100m xxx   Exponent begins with 00, significand with 100m
s 1101m xxx   Exponent begins with 01, significand with 100m
s 1110m xxx   Exponent begins with 10, significand with 100m

O bit inicial (s acima) é um bit de sinal e os bits seguintes (xxx acima) codificam os bits expoentes adicionais e o restante do dígito mais significativo, mas os detalhes variam dependendo da alternativa de codificação usada.

As combinações finais são usadas para infinitos e NaNs, e são as mesmas para ambas as codificações alternativas:

s 11110 x   ±Infinity (see Extended real number line)
s 11111 0   quiet NaN (sign bit ignored)
s 11111 1   signaling NaN (sign bit ignored)

Nos últimos casos, todos os outros bits da codificação são ignorados. Assim, é possível inicializar um array para NaNs preenchendo-o com um único valor de byte.

Campo significativo binário inteiro

Este formato usa um significando binário de 0 a 10 p −1. Por exemplo, o significando Decimal32 pode ser até 10 7 −1 = 9 999 999 = 98967F 16 = 1001 1000100101 1001111111 2 . Embora a codificação possa representar significandos maiores, eles são ilegais e o padrão requer implementações para tratá-los como 0, se encontrados na entrada.

Conforme descrito acima, a codificação varia dependendo se os 4 bits mais significativos do significando estão no intervalo de 0 a 7 (0000 2 a 0111 2 ) ou superior (1000 2 ou 1001 2 ).

Se os 2 bits após o bit de sinal forem "00", "01" ou "10", o campo de expoente consiste nos 8 bits seguintes ao bit de sinal (os 2 bits mencionados mais 6 bits de "campo de continuação de expoente") , e o significando são os 23 bits restantes, com um bit 0 inicial implícito, mostrado aqui entre parênteses:

 s 00eeeeee   (0)ttt tttttttttt tttttttttt
 s 01eeeeee   (0)ttt tttttttttt tttttttttt
 s 10eeeeee   (0)ttt tttttttttt tttttttttt

Isso inclui números subnormais em que o dígito de significando inicial é 0.

Se os 2 bits após o bit de sinal forem "11", o campo expoente de 8 bits é deslocado 2 bits para a direita (após o bit de sinal e os bits "11" posteriores), e o significando representado está no restante 21 bits. Nesse caso, há uma sequência implícita (ou seja, não armazenada) de 3 bits inicial "100" no significando verdadeiro:

 s 1100eeeeee (100)t tttttttttt tttttttttt
 s 1101eeeeee (100)t tttttttttt tttttttttt
 s 1110eeeeee (100)t tttttttttt tttttttttt

A sequência de 2 bits "11" após o bit de sinal indica que há um prefixo de 3 bits "100" implícito para o significando.

Observe que os bits iniciais do campo de significando não codificam o dígito decimal mais significativo; eles são simplesmente parte de um número binário puro maior. Por exemplo, um significand de 8 000 000 é codificada como binário 0111 1010000100 1000000000 , com os principais 4 bits que codifica 7; o primeiro significando que requer um 24º bit (e, portanto, a segunda forma de codificação) é 2 23 = 8 388 608 .

Nos casos acima, o valor representado é:

(−1) sinal × 10 expoente − 101 × significando

Decimal64 e Decimal128 operam de forma análoga, mas com continuação de expoente e campos de significandos maiores. Para Decimal128, a segunda forma de codificação nunca é usada; o maior significando válido de 10 34 -1 = 1ED09BEAD87C0378D8E63FFFFFFFF 16 pode ser representado em 113 bits.

Campo de significando decimal densamente compactado

Nesta versão, o significando é armazenado como uma série de dígitos decimais. O dígito inicial está entre 0 e 9 (3 ou 4 bits binários), e o resto do significando usa a codificação decimal densamente compactada (DPD).

Os 2 bits iniciais do expoente e o dígito inicial (3 ou 4 bits) do significando são combinados nos cinco bits que seguem o bit de sinal. Isso é seguido por um campo de continuação do expoente de deslocamento fixo.

Por fim, o campo de continuação de significando é composto por 2, 5 ou 11 declínios de 10 bits , cada um codificando 3 dígitos decimais.

Se os primeiros dois bits após o bit de sinal forem "00", "01" ou "10", então esses são os bits iniciais do expoente e os três bits seguintes são interpretados como o dígito decimal inicial (0 a 7 ):

    Comb.  Exponent          Significand
 s 00 TTT (00)eeeeee (0TTT)[tttttttttt][tttttttttt]
 s 01 TTT (01)eeeeee (0TTT)[tttttttttt][tttttttttt]
 s 10 TTT (10)eeeeee (0TTT)[tttttttttt][tttttttttt]

Se os primeiros dois bits após o bit de sinal forem "11", os segundos dois bits serão os bits iniciais do expoente, e o último bit será prefixado com "100" para formar o dígito decimal inicial (8 ou 9):

    Comb.  Exponent          Significand
 s 1100 T (00)eeeeee (100T)[tttttttttt][tttttttttt]
 s 1101 T (01)eeeeee (100T)[tttttttttt][tttttttttt]
 s 1110 T (10)eeeeee (100T)[tttttttttt][tttttttttt]

As duas combinações restantes (11110 e 11111) do campo de 5 bits são usadas para representar ± infinito e NaNs, respectivamente.

Operações aritméticas de ponto flutuante

A regra usual para realizar aritmética de ponto flutuante é que o valor matemático exato é calculado e o resultado é arredondado para o valor representável mais próximo na precisão especificada. Este é de fato o comportamento exigido para hardware de computador compatível com IEEE, sob o comportamento de arredondamento normal e na ausência de condições excepcionais.

Para facilidade de apresentação e compreensão, a precisão de 7 dígitos será usada nos exemplos. Os princípios fundamentais são os mesmos em qualquer precisão.

Adição

Um método simples para adicionar números de ponto flutuante é primeiro representá-los com o mesmo expoente. No exemplo abaixo, o segundo número é deslocado para a direita em 3 dígitos. Prosseguimos com o método de adição usual:

O exemplo a seguir é decimal, o que significa simplesmente que a base é 10.

  123456.7 = 1.234567 × 105
  101.7654 = 1.017654 × 102 = 0.001017654 × 105

Portanto:

  123456.7 + 101.7654 = (1.234567 × 105) + (1.017654 × 102)
                      = (1.234567 × 105) + (0.001017654 × 105)
                      = 105 × (1.234567 + 0.001017654)
                      = 105 × 1.235584654

Isso nada mais é do que converter para notação científica . Em detalhe:

  e=5;  s=1.234567     (123456.7)
+ e=2;  s=1.017654     (101.7654)
  e=5;  s=1.234567
+ e=5;  s=0.001017654  (after shifting)
--------------------
  e=5;  s=1.235584654  (true sum: 123558.4654)

Este é o resultado verdadeiro, a soma exata dos operandos. Ele será arredondado para 7 dígitos e depois normalizado, se necessário. O resultado final é:

  e=5;  s=1.235585    (final sum: 123558.5)

Observe que os 3 dígitos baixos do segundo operando (654) são essencialmente perdidos. Este é um erro de arredondamento . Em casos extremos, a soma de dois números diferentes de zero pode ser igual a um deles:

  e=5;  s=1.234567
+ e=−3; s=9.876543
  e=5;  s=1.234567
+ e=5;  s=0.00000009876543 (after shifting)
----------------------
  e=5;  s=1.23456709876543 (true sum)
  e=5;  s=1.234567         (after rounding/normalization)

Outro problema de perda de significância ocorre quando dois números próximos são subtraídos. e = 5; s = 1,234571 e e = 5; s = 1,234567 são representações dos racionais 123457,1467 e 123456,659.

  e=5;  s=1.234571
− e=5;  s=1.234567
----------------
  e=5;  s=0.000004
  e=−1; s=4.000000 (after rounding/normalization)

A melhor representação dessa diferença é e = −1; s = 4,877000, que difere mais de 20% de e = −1; s = 4.000000. Em casos extremos, o resultado final pode ser zero, embora um cálculo exato possa ser vários milhões. Esse cancelamento ilustra o perigo de assumir que todos os dígitos de um resultado calculado são significativos.

Lidar com as consequências desses erros são tópicos da análise numérica .

Multiplicação

Para multiplicar, os significandos são multiplicados, enquanto os expoentes são adicionados, e o resultado é arredondado e normalizado.

  e=3;  s=4.734612
× e=5;  s=5.417242
-----------------------
  e=8;  s=25.648538980104 (true product)
  e=8;  s=25.64854        (after rounding)
  e=9;  s=2.564854        (after normalization)

A divisão é feita de forma semelhante, mas isso é mais complicado.

Não há problemas de cancelamento ou absorção com a multiplicação ou divisão, embora pequenos erros possam se acumular à medida que as operações são executadas repetidamente. Na prática, a forma como essas operações são realizadas na lógica digital pode ser bastante complexa.

Veja também

Referências

Leitura adicional