Por que os computadores não armazenam números decimais como um segundo número inteiro?


24

Os computadores têm problemas para armazenar números fracionários em que o denominador é outra coisa que não uma solução para 2 ^ x. Isso ocorre porque o primeiro dígito após o decimal vale 1/2, o segundo 1/4 (ou 1 / (2 ^ 1) e 1 / (2 ^ 2)) etc.

Por que lidar com todos os tipos de erros de arredondamento quando o computador poderia ter armazenado a parte decimal do número como outro número inteiro (que é, portanto, preciso?)

A única coisa em que consigo pensar é lidar com decimais repetidos (na base 10), mas poderia ter havido uma solução de ponta para isso (como atualmente temos com o infinito).


8
Você deve procurar como os tipos decimais são armazenados, em contraste com os tipos float / double.
Oded

9
Não sei como isso é mais preciso. O primeiro dígito após o decimal é 1/10 do segundo 1/100 etc. Como é mais preciso você ainda ter problemas de arredondamento (como representa 1/3)? A única diferença é quais valores podem ser representados exatamente.
Martin York

17
O ponto flutuante decimal (que é o que você está chamando de dois, apenas em uma representação mais embaraçosa) não é mais impreciso do que o ponto flutuante binário. A única diferença é que valores não podem ser representados e, como estamos acostumados ao sistema decimal, não notamos os erros da versão decimal. E não, nenhum deles pode representar todos os números racionais e irracionais.

11
No final do dia, tudo se resume a eficiência. Os computadores são binários e os circuitos para trabalhar com essa representação binária são muito menos complexos. A importância disso pode ser um pouco diminuída hoje, mas houve uma época em que isso era muito significativo. Além disso, qualquer representação que você escolher para armazenar seu número (em um espaço finito) em um computador terá um conjunto finito de valores que pode representar e todos eles exibirão erros de arredondamento com algumas entradas. O formato típico de ponto flutuante com Mantissa e Exponent oferece uma faixa muito maior do que seria possível usando dois números inteiros.
Mr.Mindor

11
Eu recomendo a leitura de alguns dos artigos mencionados na minha resposta à pergunta O que causa erros de arredondamento de ponto flutuante? que acabei de atualizar com detalhes do último artigo da série referenciada. Em particular, dê uma olhada em Por que o ponto fixo não cura os azuis de ponto flutuante .
Mark Booth

Respostas:


35

Na verdade, existem modos de números que fazem isso.

A aritmética decimal decimal codificada em binário (BCD) faz com que o computador funcione na base 10. A razão pela qual você se depara com isso raramente é o desperdício de espaço: cada dígito individual de um número leva no mínimo quatro bits, enquanto um computador poderia armazenar até 16 valores nesse espaço. (Também pode ser mais lento, mas é possível ter uma matemática BCD acelerada por hardware que funcione perfeitamente.). Na verdade, é exatamente isso que a maioria das calculadoras faz, e é por isso que existem certas classes de problemas de arredondamento que você nunca encontrará em um Casio de US $ 5 que comerá seu almoço em um computador de mesa.

A outra rota que você pode seguir é usar números racionais - ou seja, um numerador e um denominador, armazenados como números inteiros. Na verdade, ele está disponível em quase todos os idiomas, é exato e permite armazenar tudo em formatos binários nativos. O problema é que, no final do dia, os usuários provavelmente não querem ver frações como 463/13, nem mesmo 35 e 8/13. Eles querem ver 35.615 ... e, no momento em que você chega lá, enfrenta todos os problemas típicos. Acrescente que esse formato ocupa ainda mais espaço e pode ser significativamente mais lento que a aritmética de ponto flutuante, e você não encontrará computadores que usem esse formato por padrão.

Portanto: os computadores podem fazer o que você quer, mas é lento e desperdiça espaço, então eles só fazem quando realmente precisam. No restante do tempo, a economia de velocidade e espaço do ponto flutuante é uma melhor opção.


Você não quer dizer quatro bits (não bytes) no parágrafo BCD?

3
A outra opção é a aritmética de ponto fixo, em que um número inteiro representa uma fração decimal se um número - por exemplo, Armazenar valores monetários (sem cálculos envolvendo decimais ou porcentagem) em que 1 representa $ 0,01.
mattnz

11
@mattnz: Verdadeiro - os pontos fixos são um caso especial de razões.
perfil completo de Jon Purdy

Incrível, não sabia que as calculadoras faziam isso.
SomeKittens

3
Existe uma terceira opção. Ponto flutuante com um expoente decimal, como a decimalimplementação de C # : stackoverflow.com/a/5019178/174335 Não é BCD, pois não há representação individual de dígitos decimais e não é ponto fixo.
Joren

38

Existem inúmeras maneiras de armazenar números fracionários, e cada uma delas tem vantagens e desvantagens.

O ponto flutuante é, de longe, o formato mais popular. Ele funciona codificando um sinal, uma mantissa e um expoente de base 2 assinado em números inteiros, e agrupando-os em vários bits. Por exemplo, você poderia ter uma mantissa de 32 bits de 0.5(codificada como 0x88888888) e um expoente assinado de 32 bits de +3( 0x00000003), que decodificaria para 4.0(0.5 * 2 ^ 3) Os números de ponto flutuante são rápidos, porque são implementados em hardware e sua precisão é dimensionada com tamanho absoluto, ou seja, quanto menor o número, melhor a precisão absoluta, para que o erro de arredondamento relativo permaneça constante com o tamanho absoluto. Os flutuadores são excelentes para valores amostrados de um domínio contínuo, como comprimentos, níveis de pressão sonora, níveis de luz etc., e por isso são comumente usados ​​no processamento de áudio e imagem, além de análises estatísticas e simulações de física. Sua maior desvantagem é que eles não são exatos, ou seja, são propensos a erros de arredondamento e não podem representar com precisão todas as frações decimais. Todas as linguagens de programação convencionais têm algum tipo de ponto flutuante.

Ponto fixofunciona usando números inteiros suficientemente grandes e reservando implicitamente uma parte de seus bits para a parte fracionária. Por exemplo, um número de ponto fixo de 24,8 bits reserva 24 bits para a parte inteira (incluindo sinal) e 8 bits para a parte fracionária. Mudar para a direita esse número em 8 bits nos dá a parte inteira. Os números de ponto fixo costumavam ser populares quando as unidades de ponto flutuante de hardware eram incomuns ou pelo menos muito mais lentas que suas contrapartes inteiras. Embora os números de ponto fixo sejam um pouco mais fáceis de manusear em termos de exatidão (mesmo que sejam mais fáceis de raciocinar), eles são inferiores aos flutuadores em praticamente todos os outros aspectos - eles têm menos precisão, um alcance menor e porque extra Como são necessárias operações para corrigir cálculos para a mudança implícita, hoje a matemática de ponto fixo é geralmente mais lenta que a matemática de ponto flutuante.

Os tipos decimais funcionam muito como números flutuantes ou de ponto fixo, mas eles assumem um sistema decimal, ou seja, seu expoente (implícito ou explícito) codifica potência de 10, não potência de 2. Um número decimal poderia, por exemplo, codificar uma mantissa de 23456e um expoente de -2, e isso seria expandido para234.56. Os decimais, porque a aritmética não é conectada à CPU, são mais lentos que os flutuadores, mas são ideais para qualquer coisa que envolva números decimais e precise que esses números sejam exatos, com arredondamentos ocorrendo em locais bem definidos - cálculos financeiros, placar, etc. Algumas linguagens de programação possuem tipos decimais embutidos (por exemplo, C #), outras requerem bibliotecas para implementá-las. Observe que, embora os decimais possam representar com precisão frações decimais não repetidas, sua precisão não é melhor do que a dos números de ponto flutuante; escolher decimais significa apenas obter representações exatas de números que podem ser representados exatamente em um sistema decimal (assim como os carros alegóricos podem representar exatamente frações binárias).

Os números racionais armazenam um numerador e um denumerador, normalmente usando algum tipo de número inteiro bignum (um tipo numérico que pode crescer tão grande quanto as restrições de memória do computador permitem). Esse é o único tipo de dados do grupo que pode modelar com precisão números como 1/3ou 3/17, assim como operações neles - os racionais, ao contrário dos outros tipos de dados, produzirão resultados corretos para coisas como3 * 1/3. A matemática é bastante direta, embora apresentar um algoritmo de fatoração eficiente seja bastante desafiador. Algumas linguagens de programação possuem tipos racionais embutidos (por exemplo, Common Lisp). As desvantagens dos racionais incluem que eles são lentos (muitas operações exigem redução de frações e fatoração de seus componentes), e que muitas operações comuns são difíceis ou impossíveis de implementar, e a maioria das implementações degradará o racional em flutuante quando isso acontecer (por exemplo, quando você chamar sin()de forma racional).

O BCD (decimal codificado binário) usa "petiscos" (grupos de 4 bits) para codificar dígitos individuais; como um petisco pode conter 16 valores diferentes, mas os números decimais exigem apenas 10, existem 6 valores "ilegais" por petisco. Como decimais, os números do BCD são decimais exatos, ou seja, os cálculos realizados nos números decimais funcionam da mesma maneira que funcionariam se você os fizesse usando caneta e papel. As regras aritméticas para o BCD são um pouco desajeitadas, mas a vantagem é que convertê-las em seqüências de caracteres é mais fácil do que com alguns dos outros formatos, o que é especialmente interessante para ambientes de baixo recurso, como sistemas embarcados.

As strings , sim, strings antigas simples, também podem ser usadas para representar números fracionários. Tecnicamente, isso é muito semelhante ao BCD, apenas que há um ponto decimal explícito e você usa um byte completo por dígito decimal. Como tal, o formato é um desperdício (apenas 11 dos 256 valores possíveis são usados), mas é mais fácil analisar e gerar do que o BCD. Além disso, como todos os valores usados ​​são números "não suspeitos", inofensivos e neutros em termos de plataforma, podem ser trafegados por redes sem problemas. É incomum encontrar aritmética sendo executada diretamente em strings, mas é possível, e quando você faz isso, elas são exatamente exatas como os outros formatos decimais (decimais e BCD).


Certamente o ponto fixo de 32 bits tem mais precisão do que o ponto flutuante de 32 bits, pois as representações de ponto fixo não incluem uma mantissa.
han

4
@han: Depende do tamanho do número que você deseja armazenar. Os flutuadores fornecerão (aproximadamente) a mesma precisão, não importa quão grande ou pequeno seja o número, enquanto o ponto fixo só fornecerá precisão total se o número que você deseja armazenar se encaixar perfeitamente em seu intervalo.
Leo

@han Não necessariamente, ambos ainda podem representar 2 ^ 32 valores distintos. A quantidade de informações transportadas é idêntica, independentemente da apresentação. O alcance e a precisão andam de mãos dadas, portanto, nesse aspecto, a aritmética de ponto fixo pode ser mais precisa em determinados intervalos. E evita problemas desagradáveis ​​de arredondamento aleatório, se você conhece os limites dentro dos quais pode trabalhar.
Zxcdw 03/10/12

@han: eles têm a mesma precisão (ou quase). A diferença é que, para números de ponto fixo, a precisão (como no tamanho de uma etapa discreta de um número para seu sucessor) é constante, assim como com números inteiros, enquanto que com flutuadores cresce aproximadamente linearmente com valor absoluto - o flutuador o número 1.0 tem mais precisão do que o número 10.000.000,0 (um milhão de vezes mais, aproximadamente).
tdammers

6

Os números de ponto flutuante representam uma vasta gama de valores, o que é muito útil quando você não sabe antecipadamente quais podem ser os valores, mas é um compromisso. Representar 1/10 ^ 100 com um segundo inteiro não funcionaria.

Algumas linguagens (e algumas bibliotecas) têm outras características. O Lisp tradicionalmente possui números inteiros de precisão infinitos. Cobol tem cálculos com números decimais de ponto fixo.

Você precisa selecionar sua representação numérica apropriada ao domínio do problema.


1

Parece que você está descrevendo números de ponto fixo .

Lembre-se de que armazenar a parte fracionária de um número em um local separado é precisamente idêntico a criar um espaço único, duas vezes mais longo, e armazenar a parte inteira e fracionária nas duas metades separadas. Em outras palavras, é idêntico ao armazenamento do número como um número inteiro, mas simplesmente assumindo um número fixo de espaços decimais.

Normalmente, os números de ponto flutuante são armazenados usando uma variação binária na notação científica, porque o que geralmente importa são dígitos significativos. Existem muitos outros métodos. Números decimais de ponto fixo são comumente usados, por exemplo, para armazenar valores de moeda, onde a precisão é crítica até um certo número inteiro de casas decimais, mas o número de dígitos decimais necessários nunca muda.


1

Isso se chamaria BCD, acho que você ainda pode usá-lo se realmente quiser. No entanto, não vale a pena como:

  1. Você raramente encontrará um erro de arredondamento com ponto flutuante de 64 bits
  2. Torna o complexo aritmático e ineficiente
  3. Desperdiça 6 valores a cada 4 bits

A matemática do BCD foi muito usada em sistemas de microprocessadores de 8 bits; de fato, em um microprocessador popular (o 6502), a adição e subtração com o BCD são tão rápidas por byte quanto no binário. Os videogames costumavam usar a matemática do BCD para manter a pontuação. Não há tratamento especial para empacotamento de notas em 1.000.000 pontos. Em vez disso, adicionar 1 a "99 99 99" gera "00 00 00" com um transporte que é ignorado. A sobrecarga extra de adicionar pontuações no BCD é pequena se comparada com o custo de converter um valor binário em formato de exibição.
Supercat 13/09/13

1

A resposta curta é que o ponto flutuante foi projetado para cálculos científicos. Ele pode armazenar um número com (até) um número especificado de dígitos significativos, o que se ajusta de perto à maneira como a precisão é medida na maioria dos cálculos científicos.

Isso tende a ser suportado em hardware principalmente porque os cálculos científicos tendem a ser os que mais se beneficiaram com o suporte de hardware. Por exemplo, os cálculos financeiros geralmente são feitos com outros formatos - mas o software financeiro geralmente faz pouco cálculo real o suficiente para que, embora os formatos necessários sejam suportados apenas no software, o desempenho permaneça perfeitamente adequado para a maioria dos softwares financeiros.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.