Venham de lá esses posts! Quanto ao pull request, vamos ver
A representação de números em ponto fixo é uma técnica extremamente simples e intuitiva, que eu inventei quando tinha uns 20 e andava a brincar com demos gráficas em PC - claro que eu não inventei nada
, já existia, e vim depois mais tarde a descobrir que se chamava ponto fixo.
Como é que se representa um número real, por exemplo
1.25, com números inteiros? Multiplicando por uma constante, digamos
1000, e ficando assim
1250, que é nitidamente um número inteiro. Se quisermos "recuperar" o número original, só temos que o dividir pela mesma constante:
1250 / 1000 = 1.25. Como se vê, não tem nada de especial
1250 é uma representação em ponto fixo do número
1.25 quando usamos
1000 como a "unidade":
1000, nesta representação, é
1.000 (.000 para evidenciar qtas casas decimais podemos representar), a
unidade. Podemos chamar
U (de Unidade) ao
1000, dizer que
1000 é a nossa unidade, nesta representação. Com uma unidade de
1000, o valor real mais pequeno que podemos representar com um inteiro é
0.001 .
O que fazer se o número real tem mais casas decimais do que consigo representar com a unidade escolhida, neste caso
1000? Arredondar. Por exemplo,
0.44584. Ao multiplicar por
1000 ficamos com
445.84 mas como temos que ficar com um inteiro, arredondamos para o inteiro mais próximo e ficamos com
446. Aqui estamos a criar um pequeno erro na representação; "quanto erro" é tolerável só depende da nossa aplicação. Aqui o nosso erro é inferior a +/-
0.001, que equivale a
1 em ponto fixo no nosso exemplo de
U = 1000; se precisarmos de um erro menor, teremos que usar um
U maior, por exemplo
U = 10000.
Como é que se somam 2 números em ponto fixo com
U = 1000 ("
PF1000", para simplicidade)?
Se começar com
1.25 e
0.25, tenho que os converter para
PF1000:
1.25 = 1.25 x 1000 = 1250 e
0.25 x 1000 = 250.
Podemos ver que basta adicioná-los como inteiros, não é preciso nada de especial:
1250 + 250 = 1500. Convertendo
1500, que é um
PF1000, para real, vemos que o resultado está certo:
1500 / 1000 = 1.5 (= 1.25 + 0.25)Subtrair é igualmente fácil e a mesma coisa que somar, pois subtrair é somar um número negativo.
A multiplicação é que já é um pouco diferente, mas é muito fácil perceber porquê. Vamos pegar outra vez no
1.25 e
0.25 e multiplicá-los em ponto fixo com unidade
1000 (
PF1000):
1.25 -> 1250
0.25 -> 250
1250 x 250 = 312500Se convertermos
312500 "de volta" para real, ficamos com
312.5, quando o resultado deveria ser
1.25 x 0.25 = 0.3125. O que é que aconteceu? O que aconteceu foi que pelo caminho criámos um resultado em
PF1000000! Para mostrar como, em vez de usar
1250 e
250 para representar os números em
PF1000, vou usar
(1.25 x U) e
(0.25 x U), e então fica evidente:
(1.25 x U) x (0.25 x U)
= 1.25 x U x 0.25 x U
= 1.25 x 0.25 x U x U
= 0.3125 x U x Uou seja acabámos com o resultado (
0.3125) multiplicado pelo
quadrado da unidade,
U x U, e não pela unidade
U como se quer. Como resolver isto? Muito simples, basta dividir o resultado por
U, e voltamos a ter o número em
PF1000
0.3125 x U x U
--------------- = 0.3125 x U = 0.3125 x 1000 = 312.5 -> 313 (arredondado)
UPortanto para multiplicar 2 números que estão em ponto fixo temos que multiplicá-los e dividir o resultado pela unidade
U da representação.
Na divisão de numeros em PF ocorre um "problema" semelhante ao que acontece na multiplicação. Se fizermos
1250 / 250 ficamos com
5, quando o resultado em
PF1000 deveria ser
(1.25 / 0.25) x 1000 = 5000. O que aconteceu foi que, na divisão, dividimos a Unidade por ela própria, eliminando-a:
1.25 x U 1.25
---------- = ------ = 5
0.25 x U 0.25Para resolver isto, temos que multiplicar o dividendo pela unidade
U,
antes de fazer a divisão:
1.25 x U x U 1.25 x U
-------------- = ---------- = 5 x U = 5 x 1000 = 5000
0.25 x U 0.25Espero que se entenda
. Se alguém quiser discutir sou todo ouvidos.
Há ainda 3 detalhes práticos, sobre melhorar a precisão nas divisões (a divisão inteira dos CPUs é uma divisão por defeito, o que faz com que o resultado não seja sempre o número inteiro mais próximo do número real), velocidade de cálculo (dividir/multiplicar por 1000 é relativamente lento num CPU, mas e se for por uma potência de 2?) e tamanho da representação intermédia (quando dividimos números em PF temos um valor intermédio que tem
U2, e isso ocupa muito "espaço", bits, que não precisamos para as representações finais em PF), mas por agora já me alonguei mais do que queria