Ver la versión completa : Ayuda con aritmetica de punto fijo (fixed point)
Ya me explicasteis una vez un poco sobre el tema del punto fijo, pero no acabé de entenderlo muy bien y supongo que se ha perdido por el efecto 20k.
¿Podríais explicarme en qué consiste y cómo funciona la aritmética de punto fijo?
Necesito convertir una clase cVector3D que trabaja con floats, a otra de punto fijo.
En ésta clase hay restas, multiplicaciones, divisiones, comparaciones de igualdad ==, raices cuadradas sqrt(), el operador *= y /= , sin(), cos(), y creo que no me dejo ninguno. ¿Puedo hacer todo ésto con punto fijo? Supongo que sí, pero ¿como?
:lovegp2x:
Estopero
31/07/2007, 15:02
Te doy la explicación pa tontos que es lo que se me quedo a mi de lo q me explicaron xD, supongo q cuando me ponga a meterle el puntofijo a mi juego lo entendere mejor :S
Todo se puede hacer con punto fijo, el unico problema es que pierdes precision de decimales, en vez de poder tener 16 decimales pues tienes 4 o 2, que para la mayoria de calculos es mas que suficiente, y ganas que los tratas como enteros y no como floats, guay para maquinitas que se resientan al usarlos =). Como se hace? uhmmmm ni idea xD no recuerdo :S
Gracias Estopero
Creo que voy a usar esto como referencia: http://wiki.yak.net/675
Es una librería muy simple que usa una clase llamada Fixed con todos los operadores posibles sobrecargados para funcionar en punto fijo.
Salustian
31/07/2007, 15:40
Por lo que tengo entendido, si no es así que me corrijan, si añades la opción -ffast-math a GCC ya estarías trabajando en punto fijo, con lo que no tendrías que tocar nada en tu código.
De todas formas, creo que fue Chui el que posteo una biblioteca de funciones para trabajar en punto fijo que usaba enteros de 64 bits (signed long long). Aquí la tienes como adjunto.
Un saludo.
Puck2099
31/07/2007, 15:50
Y si usas la de Chui además verás que vienen las funciones en ensamblador de x86 y ARM, es lo que yo uso en el Fenix ;)
Muy interesante pero...no parece muy portable, aunque he visto que también tiene versiones de las funciones en C sin asm.
La cuestion es que quiero hacer funcionar mi motor en máquinas muy dispares
¿Y respecto a la licencia?
EDIT: olvidad lo de maquinas dispares. El punto fijo solo es para ARM :brindis: el resto de plataformas usan coma flotante por lo que no usarían el código de chui :)
Puck2099
31/07/2007, 16:35
EDIT: olvidad lo de maquinas dispares. El punto fijo solo es para ARM :brindis: el resto de plataformas usan coma flotante por lo que no usarían el código de chui :)
Claro, para máquinas con FPU es mejor no usar el punto fijo porque encima pierdes velocidad con las conversiones :)
mortimor
31/07/2007, 17:11
La licencia supongo que sera GPL 2. Por lo menos una version de las mismas que usabamos en GP32 ya la llevaba :)
dr_bacterio
31/07/2007, 21:05
Buenas a todooooooooos,
Sobre punto fijo, vi un articulo en la antigua pagina de desarrollo de videojuegos FLIPCODE, aqui te dejo el enlace por si le quieres hechar un vistazo:
http://www.flipcode.com/articles/article_mobilegfx02.shtml
enga saludos y animos con tu proyecto ;)
< - >
Y otros más que encontré .....
jejeje
http://gameprogrammer.com/4-fixed.html
http://www.devmaster.net/articles/fixed-point-optimizations/
http://trac.bookofhook.com/bookofhook/trac.cgi/wiki/IntroductionToFixedPointMath
http://www.embedded.com/showArticle.jhtml?articleID=15201575
Vaya, no sabía que en flipcode tenían un artículo al respecto. Me encanta esa web, tiene artículos muy buenos e interesantes.
Gracias por los enlaces
En serio es necesario que uses librerias? Tanto te cuesta comprender que es el punto fijo? No es nada megasofisticado, es el apaño que se invento alguien para guardar unos pocos decimales en un entero.
Vamos a ver si te mola esta explicación. xD
Ok, tienes una variable que puedes guardar solo enteros, ok? Resulta que por cualquier circunstancia te iria bien guardar medias partes en esta variable que solo guardas enteros. Lo que haces es por ejemplo imaginarte que el 10 es equivalente a 1 (sí, sí, te lo imaginas), es decir, 10 es la unidad, y que si quieres representar el 0'5, pues logicamente en el sistema que te has inventado tu seria 5. Pero este sistema que me acabo de sacar de la patilla es un poco impreciso, no? No puedes guardar por ejemplo 0'15. Para arreglar eso podriamos de nuevo imaginarnos que 100 es la unidad, o 1000, o cualquiera, si es mas grande, mas decimales tendras, pero piensa que en una variable hay un numero maximo que puedes almacenar, por lo que si pones un numero demasiado grande como unidad no podras representar numeros más grandes.
Bien, el sistema decimal es muy bonito para los humanos, pero las maquinas son binarias, y el hecho de decir que 1000 es la unidad no es muy practico porque si por ejemplo quisieses saber la parte entera, deberias dividir por 1000, y eso es mas lento que dividir por potencias de dos. Se supone que como buen informatico que eres, sabes que desplazando los bits de una variable hacia la izquierda o hacia la derecha puedes multiplicar o dividir por potencias de dos. Así que de ahí que se usen desplazamientos de bits como (1<<6). Imaginate que en vez de poner 1000 como la unidad, ponemos 1024, tambien conocido como 2^10 o en C++ (1<<10). Si quieres obtener la parte entera, con un simple (variable>>10) podrias obtenerlo.
Aquí se acaba mi lección, alguna duda? Lo demás imaginatelo.
Estopero
31/07/2007, 22:40
En serio es necesario que uses librerias? Tanto te cuesta comprender que es el punto fijo? No es nada megasofisticado, es el apaño que se invento alguien para guardar unos pocos decimales en un entero.
Vamos a ver si te mola esta explicación. xD
Ok, tienes una variable que puedes guardar solo enteros, ok? Resulta que por cualquier circunstancia te iria bien guardar medias partes en esta variable que solo guardas enteros. Lo que haces es por ejemplo imaginarte que el 10 es equivalente a 1 (sí, sí, te lo imaginas), es decir, 10 es la unidad, y que si quieres representar el 0'5, pues logicamente en el sistema que te has inventado tu seria 5. Pero este sistema que me acabo de sacar de la patilla es un poco impreciso, no? No puedes guardar por ejemplo 0'15. Para arreglar eso podriamos de nuevo imaginarnos que 100 es la unidad, o 1000, o cualquiera, si es mas grande, mas decimales tendras, pero piensa que en una variable hay un numero maximo que puedes almacenar, por lo que si pones un numero demasiado grande como unidad no podras representar numeros más grandes.
Bien, el sistema decimal es muy bonito para los humanos, pero las maquinas son binarias, y el hecho de decir que 1000 es la unidad no es muy practico porque si por ejemplo quisieses saber la parte entera, deberias dividir por 1000, y eso es mas lento que dividir por potencias de dos. Se supone que como buen informatico que eres, sabes que desplazando los bits de una variable hacia la izquierda o hacia la derecha puedes multiplicar o dividir por potencias de dos. Así que de ahí que se usen desplazamientos de bits como (1<<6). Imaginate que en vez de poner 1000 como la unidad, ponemos 1024, tambien conocido como 2^10 o en C++ (1<<10). Si quieres obtener la parte entera, con un simple (variable>>10) podrias obtenerlo.
Aquí se acaba mi lección, alguna duda? Lo demás imaginatelo.
Tio me ha encantao ^^, con la de veces q me lo has intentao explicar por msn xDDD.
Entonces con un ejemplo practica la cosa seria asi:
Tengo mi personaje en la posicion x=100 y quiero que avance 1,78 pixeles por frame (iba a poner 1,5 pero entonces haria otros metodos xD) entonces lo que haria para poder usar punto fijo seria convertir (no convertir cada vez, sino cambiar el modo en el q operan las funciones) las coordenadas, ahora su propiedad x pasaria a valer 10000, y yo tendria que añadirle 178 en cada frame, pero a la hora de pintarlo en pantalla haria esto:
funcion_para_aplicar_superficie(personaje.x>>100, personaje.y>>100, blablabla)
es asi? creo q he fallao en el tema de las potencias de 2, pero al menos por ahi van los tiros xD
Tio me ha encantao ^^, con la de veces q me lo has intentao explicar por msn xDDD.
Entonces con un ejemplo practica la cosa seria asi:
Tengo mi personaje en la posicion x=100 y quiero que avance 1,78 pixeles por frame (iba a poner 1,5 pero entonces haria otros metodos xD) entonces lo que haria para poder usar punto fijo seria convertir (no convertir cada vez, sino cambiar el modo en el q operan las funciones) las coordenadas, ahora su propiedad x pasaria a valer 10000, y yo tendria que añadirle 178 en cada frame, pero a la hora de pintarlo en pantalla haria esto:
funcion_para_aplicar_superficie(personaje.x>>100, personaje.y>>100, blablabla)
es asi? creo q he fallao en el tema de las potencias de 2, pero al menos por ahi van los tiros xD
Jeje, casi pero no, en tu caso seria:
funcion_para_aplicar_superficie(personaje.x/100, personaje.y/100, blablabla)
porque interpretas que 100 es 1, y si quieres tener la parte entera necesitas dividir por 100, logicamente. Lo de los desplazamientos de bits es solo para hacer divisiones de numeros en potencia de 2 en caso de querer mas eficiencia, pero si no haces calculos muy intensivos podrias prescindir de usar esto. Si tubieras en vez de 100 pues 128 seria otro cantar, porque 128 es 2^7, asi que con un simple (variable>>7) dividirias por 128 de una forma casi inmediata.
Bueno, pero la pregunta es.... para que utilizar punto fijo
¿? Para ganar velocidad en maquina que no tengan fpus¿?
Bueno, pero la pregunta es.... para que utilizar punto fijo
¿? Para ganar velocidad en maquina que no tengan fpus¿?
Exacto. Porque si usase coma flotante en una maquina sin fpu, la coma flotante se tendría que emular por lo tanto mucho más lento.
Exacto. Porque si usase coma flotante en una maquina sin fpu, la coma flotante se tendría que emular por lo tanto mucho más lento.
xo klaro, con punto fijo pierdes bastante precision, pero si quieres ganar precision puede que no puedas escribir un numero grande, a no se que utilices la lib esa con enteros de 64 bits.
Por ejemplo, en cualquier programa mio como podria utilizar el punto fijo¿? Cambiando solo donde hay un float para hacer estos trukitos¿?Como multiplico dos numeros de punto fijo¿?
xo klaro, con punto fijo pierdes bastante precision, pero si quieres ganar precision puede que no puedas escribir un numero grande, a no se que utilices la lib esa con enteros de 64 bits.
Por ejemplo, en cualquier programa mio como podria utilizar el punto fijo¿? Cambiando solo donde hay un float para hacer estos trukitos¿?Como multiplico dos numeros de punto fijo¿?
Multiplicar? No es mucho mas dificil. Si por ejemplo tienes una variable que has puesto que 10 es equivalente a 1 por ejemplo (esta en base 10), y en esta tienes 0'5, es decir 5. Imaginate que queremos multiplicar por cualquier entero que no sea de coma fija como por ejemplo 2.
5 * 2 = 10; es correcto porque 0'5 * 2 = 1;
Pero que pasa si queremos multiplicar 2 numeros en coma fija, pongamos de nuevo el caso que tenemos 2 variables donde 10 es equivalente a 1 y en ambas tenemos 0'5, es decir, 5.
5 * 5 = 25; no es correcto porque 0'5 * 0'5 = 0'25;
Lo que ocurre es un poco lo que pasa cuando eres pequeño y te enseñan a multiplicar y dividir, te acuerdas eso de "Cuando se multiplican 2 numeros con parte fraccionaria, primero se multiplican tal cual y luego se cuentan el numero de cifras que hay detras de la coma de ambos numeros para saber donde cae la coma en el nuevo numero calculado".
En este caso si teniamos dos variables de base 10, lo que han hecho en verdad es multiplicarse ambas bases, es decir, el calculo real seria como este:
bases: 10 * 10 = 100;
numer: 5 * 5 = 25;
es correcto porque 25 en base 100 es 0,25. Pero esto no es el resultado que queriamos, nosotros si lo que queremos es multiplicar dos variables en base 10, queremos el resultado en base 10, asi que procedamos:
bases: (10 * 10) / 10 = 10;
numer: ( 5 * 5) / 10 = 2;
Voilá. El resultado de (0'5 * 0'5) en base 10 es 0'2 logicamente porque no tenemos mas precisión.
En este tipo de operaciones, cojer bases en potencias de 2 acelera mucho el calculo porque logicamente las divisiones y multiplicaciones con estos numeros como he dicho antes son inmediatas.
Multiplicar? No es mucho mas dificil. Si por ejemplo tienes una variable que has puesto que 10 es equivalente a 1 por ejemplo (esta en base 10), y en esta tienes 0'5, es decir 5. Imaginate que queremos multiplicar por cualquier entero que no sea de coma fija como por ejemplo 2.
5 * 2 = 10; es correcto porque 0'5 * 2 = 1;
Pero que pasa si queremos multiplicar 2 numeros en coma fija, pongamos de nuevo el caso que tenemos 2 variables donde 10 es equivalente a 1 y en ambas tenemos 0'5, es decir, 5.
5 * 5 = 25; no es correcto porque 0'5 * 0'5 = 0'25;
Lo que ocurre es un poco lo que pasa cuando eres pequeño y te enseñan a multiplicar y dividir, te acuerdas eso de "Cuando se multiplican 2 numeros con parte fraccionaria, primero se multiplican tal cual y luego se cuentan el numero de cifras que hay detras de la coma de ambos numeros para saber donde cae la coma en el nuevo numero calculado".
En este caso si teniamos dos variables de base 10, lo que han hecho en verdad es multiplicarse ambas bases, es decir, el calculo real seria como este:
bases: 10 * 10 = 100;
numer: 5 * 5 = 25;
es correcto porque 25 en base 100 es 0,25. Pero esto no es el resultado que queriamos, nosotros si lo que queremos es multiplicar dos variables en base 10, queremos el resultado en base 10, asi que procedamos:
bases: (10 * 10) / 10 = 10;
numer: ( 5 * 5) / 10 = 2;
Voilá. El resultado de (0'5 * 0'5) en base 10 es 0'2 logicamente porque no tenemos mas precisión.
En este tipo de operaciones, cojer bases en potencias de 2 acelera mucho el calculo porque logicamente las divisiones y multiplicaciones con estos numeros como he dicho antes son inmediatas.
Vale, esta claro, pero se nota mejoria en el rendimiento utilizando esto¿? No es que haya hecho nada en gp2x ke necesitase tales calculos creo, pero va bien saberlo por si akaso un dia me decido ha hacer algo.
< - >
Vale, esta claro, pero se nota mejoria en el rendimiento utilizando esto¿? No es que haya hecho nada en gp2x ke necesitase tales calculos creo, pero va bien saberlo por si akaso un dia me decido ha hacer algo.
Lo que no veo nada claro es el tema de poner esto en mi codigo ya escrito utilizando floats.
Vale, esta claro, pero se nota mejoria en el rendimiento utilizando esto¿? No es que haya hecho nada en gp2x ke necesitase tales calculos creo, pero va bien saberlo por si akaso un dia me decido ha hacer algo.
< - >
Lo que no veo nada claro es el tema de poner esto en mi codigo ya escrito utilizando floats.
Se va notar si tienes un uso intenso de coma flotante y los pasas todos a coma fija. Si se nota en el rendimiento? Una multiplicacion de coma fija no creo que sean poco mas de 4 instrucciones. En coma flotante no se si sabes como funciona pero entre que extraes el signo, exponente y la mantisa, haces las operaciones y lo vuelves a juntar todo pues algo sí se deberia notar, no?
Se va notar si tienes un uso intenso de coma flotante y los pasas todos a coma fija. Si se nota en el rendimiento? Una multiplicacion de coma fija no creo que sean poco mas de 4 instrucciones. En coma flotante no se si sabes como funciona pero entre que extraes el signo, exponente y la mantisa, haces las operaciones y lo vuelves a juntar todo pues algo sí se deberia notar, no?
Hombre si, algo se tendria ke notar, y mas en la gp2x que no tiene fpu.
Lo que te decia, es ke a ver, si tengo este codigo (es un poco inutil xo bueno)
float pi = 3.1415;
mover_personaje(cpu, x, y, pi*35.24);
en este codigo tan tonto que tendria que haceR¿? pasar pi a entero, guardarlo enuna variable, luego hacer la multiplicacion con el 35.24 pasado a entero y luego recovnetirlo a float para que la funcion sepa el numero¿?
Estopero
01/08/2007, 14:42
Hombre si, algo se tendria ke notar, y mas en la gp2x que no tiene fpu.
Lo que te decia, es ke a ver, si tengo este codigo (es un poco inutil xo bueno)
float pi = 3.1415;
mover_personaje(cpu, x, y, pi*35.24);
en este codigo tan tonto que tendria que haceR¿? pasar pi a entero, guardarlo enuna variable, luego hacer la multiplicacion con el 35.24 pasado a entero y luego recovnetirlo a float para que la funcion sepa el numero¿?
Pues habria varias formas de punto fijo, la decimal o la de en potencias de dos son las q yo he usado(ayer) XD, la decimal tiene el punto bueno de que es mucho mas comprensible directamente, la de potencias de dos salen numeros q no tienen porq parecer a primera vista el numero que pretendes usar pero es mas rapido aun
DECIMAL:
int pi = 31415;
Mover_personaje(cpu,x,y,(pi*352400);
*X e Y tendrian que estar tb en punto fijo multiplicados por 10000, y ya al final dentro de la funcion cuando pintas el grafico en pantalla divides el resultado por 10000
BINARIO: Para poder representar 4 decimales necesitas 14 bits, que seran los que estan en cursiva, luego la parte entera la pones a la izquierda
Pseudo:
pi = 3,1415 = 1100010110000111 -> 50567 -> Lo que esta en negrita es la parte entera, lo que esta en cursiva la parte decimal, se calcula los binarios por separado
35,2400 = 10001100100101100000 -> 575840
Quedaria:
int pi = 50567;
int otroNumero = 575840
mover_personaje(cpu,x,y,((pi*otroNumero )>>14)
Al desplazar 14 bits el numero te quedarias con la parte entera
No se si estaria bien asi, ¿Bud? XD, pues no he hecho el calculo y esta mal, ***** >_<, porque claro en binario la parte entera y decimal se multiplica por separado
Cualquiera de las dos opciones es mas rapida en una maquina sin FPU, pero si lo haces con el metodo de potencias de dos pues ganas otro porcentaje mas rendimiento =)
BINARIO: Para poder representar 4 decimales necesitas 14 bits, que seran los que estan en cursiva, luego la parte entera la pones a la izquierda
Pseudo:
pi = 3,1415 = 1100010110000111 -> 50567 -> Lo que esta en negrita es la parte entera, lo que esta en cursiva la parte decimal, se calcula los binarios por separado
35,2400 = 10001100100101100000 -> 575840
Quedaria:
int pi = 50567;
int otroNumero = 575840
mover_personaje(cpu,x,y,((pi*otroNumero )>>14)
Al desplazar 14 bits el numero te quedarias con la parte entera
No se si estaria bien asi, ¿Bud? XD, pues no he hecho el calculo y esta mal, ***** >_<, porque claro en binario la parte entera y decimal se multiplica por separado
Cualquiera de las dos opciones es mas rapida en una maquina sin FPU, pero si lo haces con el metodo de potencias de dos pues ganas otro porcentaje mas rendimiento =)
Uhm, que has hecho ahi? xD
A ver, como te dige, usando como base numeros binarios salen numeros raros. xD
Tu mientras confies en la teoria sabes que todo saldra bien. Si quieres obtener 3'1415 en coma fija donde la base por ejemplo es 2^16 (unsigned int, 16 bits de parte entera, y 16 de parte decimal, es la mas usada normalmente), lo que haria es precalcular PI y luego lo añadiria como constante en un #define.
Para calcular PI podriamos hacer lo siguiente:
variable = 31415 << 16; //Ahora tenemos 31415 en base 2^16
variable /= 10000; //Dividimos por 10.000 y supuestamente tenemos 3'1415.
Representamos la variable:
Valor en decimal: 205881
Binario: 00000000000000110010010000111001
La parte entera la pongo en negrita, como puedes ver, son efectivamente 16 de parte entera y 16 bits de parte decimal. Por quien dude, la parte decimal la podeis calcular de esta forma:
El primer bit de la izquiera vale la mitad de 1, el siguiente la mitad, el otro la mitad, y asi:
0'5 , 0'25, 0'125, 0'0625, 0'03125, 0'015625...
2^(-1), 2^(-2), 2^(-3), 2^(-4), 2^(-5), 2^(-6)...
A mi me da que el resultado es:
0,1414947509765625
Segun parece, esa es la aproximación que ofrece este sistema. xD
Efectivamente se puede representar una burrada de decimales, pero no todos, solo los que se pueden calcular a base de sumar las mitades que he puesto antes (logico, no?). La coma flotante en parte trabaja así. Si usasemos enteros de 64 bits, podriamos tener mas parte entera (con 16 bits de parte entera solo podemos representar hasta el numero 65536) y tener muchisimas combinaciones de parte decimal y a la vez tener más precisión, pero mientras no haga falta una precision megaextrema este sistema hará su función. Muchas de las librerias que hay por ahi de coma fija trabajan con variables de 64 bits, pero piensa que los micros ARM no son de 64 bits asi que estas tienen que ir usando dos variables de 32 bits y esto lo hace algo mas lento.
Aqui acaba otra lección. Ahora solo falta que alguien se las lea. ~_~'
PD: Si lo calculas con la calculadora de windows, esta dice que el numero correcto seria 205881'344. Como solo trabajamos con enteros el 0'344 se elimina, por eso no se puede representar con exactitud el numero, eso es el error que se comete.
EDIT1
Yo de vosotros usaria mejor 205887 (calculado a partir de la aprox. 3'14159265) en vez de 205881, que este es equivalente a 3'1415863037109375 y se parece más a PI.
Exacto. Porque si usase coma flotante en una maquina sin fpu, la coma flotante se tendría que emular por lo tanto mucho más lento.
y que tipo de emulacion hace compilador para ser menos eficiente que la que podemos hacer nosotros a mano?
lo suyo seria sencillamente que el emulador internamente convirtiera las operaciones flotantes a conjuntillo de instrucciones fijas que estamos haciendo nosotros para emular.
es decir, no deberia quedar los mismo emulando nosotros que emulando el compilador? o es que la emulacion del compilador es diferente y mas mala?
Aiken
Estopero
02/08/2007, 16:10
y que tipo de emulacion hace compilador para ser menos eficiente que la que podemos hacer nosotros a mano?
lo suyo seria sencillamente que el emulador internamente convirtiera las operaciones flotantes a conjuntillo de instrucciones fijas que estamos haciendo nosotros para emular.
es decir, no deberia quedar los mismo emulando nosotros que emulando el compilador? o es que la emulacion del compilador es diferente y mas mala?
Aiken
pues como bien ha dicho salustian,creo que el compilador es capaz de pasar a punto fijo mediante el flag -ffast-math, y la verdad es que no se exactamente la diferencia de rendimiento entre la forma automática y la manual,lo que si sé es que intente la automatica y cascaba todo XDDDD, tb supongo que un método universal que tiene que valer para todo como hara el compilador para pasar a punto fijo,será mas lento que el metodo de Bud de las potencias de dos, pero claro como yo no consigo hacerlo de ninguna de las maneras.... XDDD. un saludo!
Ahora solo falta que alguien se las lea. ~_~' ¿Leer? ¿El qué? :D Casualidades de la vida, hoy ha sido la primera vez que he tenido que utilizar punto fijo, en el trabajo. Tenía una idea de lo que era, pero nunca lo había aplicado.
¿Leer? ¿El qué? :D Casualidades de la vida, hoy ha sido la primera vez que he tenido que utilizar punto fijo, en el trabajo. Tenía una idea de lo que era, pero nunca lo había aplicado.
Fua Soler, solo con tu lectura ya valio la pena escribirlo!! [wei] [wei]
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.