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 23456
e 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/3
ou 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).