Hoje vou lhe dizer o que é ponto fixo, por que é necessário e como pode ser usado.
Existe um problema quando o desempenho do aplicativo pode deteriorar-se visivelmente devido às peculiaridades dos cálculos de ponto flutuante. Como regra, a CPU é afiada para operações inteiras, e o coprocessador FPU (unidade de ponto flutuante) nele trabalha em uma ordem mais lenta. Existem plataformas onde não há FPU e emular operações com números levaria muito tempo. Por exemplo, na presença de FPU, a multiplicação de números de ponto flutuante é realizada por apenas um comando fmul e, na ausência de FPU, a multiplicação é realizada pela função de emulação __mulsf3. Comparada ao comando fmul, a função __mulsf3 emula operações em números de ponto flutuante, enquanto os cálculos são executados em forma de número inteiro, o que leva a um aumento no código da máquina e no tempo necessário para executar, enquanto o comando fmul executará essa operação rapidamente, usando hardware fundos.
Existe uma solução para esse problema, que permite realizar cálculos com um ponto fixo em um tipo inteiro.
O princípio deste tipo é um deslocamento fixo do número por N bits, como resultado do qual o número fracionário pode ser representado como um número inteiro e terá uma precisão de 2 ^ N após o ponto. Um exemplo de conversão de um número de ponto flutuante em um número de ponto fixo da ordem de 8 bits (2 ^ 8 = 1024).
Aqui está um exemplo de conversão de um número de ponto flutuante em um número de ponto fixo:
Fixed(12345,6789) = 1024 * 12345,6789 = 12641975,<s>1936</s>
Esse número, após o ponto, tem uma precisão de 2 ^ 8 após o ponto decimal.
Um exemplo da conversão reversa de um número de ponto fixo para um número de ponto flutuante.
Float(12641975) = 12641975 / 1024 = 12345,678<s>7109375</s>
Nesse caso, o número após a conversão reversa tem o formato 12345.6787109375 e é preciso 3 dígitos após o período, a precisão máxima é realmente 2 ^ 8 = 1024.
Como ocorrem os cálculos em um tipo de ponto fixo?
As operações de soma e diferença são equivalentes a operações inteiras comuns.
Fixed(x) + Fixed(y) Fixed(x) - Fixed(y)
, em qualquer ordem
(1024 * x) + (1024 * y) (1024 * x) - (1024 * y)
A multiplicação de tais números é feita nesta forma.
(Fixed(x) * Fixed(y)) / p
, isso é equivalente, com uma ordem de 8 bits
((1024 * x) * (1024 * y)) / 1024
Divisão.
(Fixed(x) * p) / Fixed(y)
, também na ordem de 8 bits, este
(1024 * 1024 * x)*(1024 * y)
Estouro
Ao executar operações de multiplicação e divisão, é possível um caso de estouro, o que levará a um resultado incorreto. Isso acontecerá se, por exemplo, um tipo inteiro de 32 bits for usado e durante os cálculos ocorrer um estouro desse tipo e, como resultado desse estouro, o número perderá os bits mais significativos. Existem duas maneiras de eliminar o estouro:
- Execute cálculos em um tipo inteiro de 64 bits.
- Execute cálculos em uma forma “desmontada”, por exemplo, ao multiplicar, (xi + xf) * (yi + yf) = xi * yi + xf * yf + xi * yf + yi * xf, os prefixos ie f significam a parte inteira e a parte após pontos.
Classe para trabalhar com ponto fixo em C ++
#define DIGITS 1024 // #define EPS 20 // using namespace std; typedef signed int __int32_t; class Fixed { signed int x; Fixed(signed int a){ x = a; } public: Fixed(){ x = 0; } static Fixed fromInt(signed int val){ return Fixed(val*DIGITS); } static Fixed fromFloat(float val){ return Fixed((signed int)(val*DIGITS)); } float fixed2float(){ return ((float)x)/DIGITS; } Fixed sum(Fixed a,Fixed b){ return Fixed(a.x+bx); } Fixed diff(Fixed a,Fixed b){ return Fixed(ax-bx); } Fixed mul(Fixed a,Fixed b){ signed int c=ax*bx; if(c/bx != ax){ // Overflow! signed int i1 = ax/DIGITS; signed int i2 = bx/DIGITS; signed int f1 = (ax&(DIGITS-1)); signed int f2 = (bx&(DIGITS-1)); return Fixed((i1*i2)*DIGITS+(f1*f2)/DIGITS+i1*f2+i2*f1); }else{ return Fixed(c/DIGITS); } } Fixed div(Fixed a,Fixed b){ if(ax>(1<<21)){ // Overflow! signed int i = ax/DIGITS; signed int f = (ax&(DIGITS-1)); return Fixed(((i*DIGITS)/bx)*DIGITS+(f*DIGITS)/bx); }else{ return Fixed((ax*DIGITS)/bx); } } Fixed sqrt(Fixed k){ Fixed tmp(0); tmp.x = kx/2; signed int min = 0; signed int max = kx; Fixed quick(0); do{ tmp.x = (min+max)/2; quick = Fixed::mul(tmp,tmp); if(abs(quick.xk.x)<EPS) return Fixed(tmp); if(quick.x>kx){ max = tmp.x; }else{ min = tmp.x; } }while(true); } };