Hoy les diré qué es el punto fijo, por qué es necesario y cómo se puede usar.
Existe tal problema cuando el rendimiento de la aplicación puede deteriorarse notablemente debido a las peculiaridades de los cálculos de coma flotante. Como regla general, la CPU se agudiza para operaciones enteras, y el coprocesador FPU (unidad de punto flotante) funciona a un orden más lento. Existen plataformas en las que no hay FPU y la emulación de operaciones con números llevaría mucho tiempo. Por ejemplo, en presencia de FPU, la multiplicación de números de coma flotante se realiza mediante un solo comando fmul, y en ausencia de FPU, la función de emulación __mulsf3 realiza la multiplicación. En comparación con el comando fmul, la función __mulsf3 emula operaciones en números de coma flotante, mientras que los cálculos se realizan en forma de enteros, lo que conduce a un aumento en el código de máquina y el tiempo que lleva ejecutar, mientras que el comando fmul realizará esta operación rápidamente, utilizando hardware fondos
Hay una solución a este problema, que permite realizar cálculos con un punto fijo en un tipo entero.
El principio de este tipo es un desplazamiento fijo del número por N bits, como resultado de lo cual el número fraccionario puede representarse como un número entero y tendrá una precisión de 2 ^ N después del punto. Un ejemplo de conversión de un número de coma flotante a un número de coma fija del orden de 8 bits (2 ^ 8 = 1024).
Aquí hay un ejemplo de conversión de un número de coma flotante a un número de coma fija:
Fixed(12345,6789) = 1024 * 12345,6789 = 12641975,<s>1936</s>
Este número, después del punto, tiene una precisión de 2 ^ 8 después del punto decimal.
Un ejemplo de la traducción inversa de un número de punto fijo a un número de punto flotante.
Float(12641975) = 12641975 / 1024 = 12345,678<s>7109375</s>
En este caso, el número después de la traducción inversa tiene la forma 12345.6787109375 y es exacto 3 dígitos después del período, la precisión máxima es en realidad 2 ^ 8 = 1024.
¿Cómo se realizan los cálculos en un tipo de punto fijo?
Las operaciones de suma y diferencia son equivalentes a las operaciones enteras ordinarias.
Fixed(x) + Fixed(y) Fixed(x) - Fixed(y)
, en cualquier orden
(1024 * x) + (1024 * y) (1024 * x) - (1024 * y)
La multiplicación de tales números se realiza de esta forma.
(Fixed(x) * Fixed(y)) / p
, esto es equivalente, con un orden de 8 bits
((1024 * x) * (1024 * y)) / 1024
División
(Fixed(x) * p) / Fixed(y)
, también con un orden de 8 bits, esto
(1024 * 1024 * x)*(1024 * y)
Desbordamiento
Al realizar operaciones de multiplicación y división, es posible un caso de desbordamiento, lo que conducirá a un resultado incorrecto. Esto sucederá si, por ejemplo, se usa un tipo entero de 32 bits y durante los cálculos se producirá un desbordamiento de este tipo y, como resultado de este desbordamiento, el número perderá los bits más significativos. Hay dos formas de eliminar el desbordamiento:
- Realice cálculos en un tipo entero de 64 bits.
- Realice cálculos en una forma "desmontada", por ejemplo, al multiplicar, (xi + xf) * (yi + yf) = xi * yi + xf * yf + xi * yf + yi * xf, los prefijos i y f significan la parte completa y la parte posterior puntos.
Clase para trabajar con punto fijo en 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); } };