Parser hexadecimal
A ver si animamos un poco el Codigo de la Semana que esta muy aburido ultimamente
mando este trozo de codigo que estuve escribiendo el otro dia, del cual no estoy muy contento porque estoy seguro que se puede hacer mejor... si alguien se anima que me de su version mejorada y se lo agradecere enormemente con mujeres y vino XDDD
la idea es convertir una cadena que representa un DWORD en una variable DWORD propiamente dicha.
Esto es muy util cuando tienes un fichero de ascii con valores que quieres introducir en tu juego, digamos un fichero INI, XML o lo que sea...
FOG_COLOR = FFAAAA11
al leer el fichero obtenemos una cadena "FFAAAA11" pero queremos meter este valor en un DWORD para pasarselo a DX por ejemplo.
typedef unsigned long DW; //es lo mismo que el DWORD definido en windows.h
inline unsigned char __CHAR2BYTE (unsigned char c)
{
switch (c)
{
case '0': return 0x0;
case '1': return 0x1;
case '2': return 0x2;
case '3': return 0x3;
case '4': return 0x4;
case '5': return 0x5;
case '6': return 0x6;
case '7': return 0x7;
case '8': return 0x8;
case '9': return 0x9;
case 'A': return 0xA;
case 'B': return 0xB;
case 'C': return 0xC;
case 'D': return 0xD;
case 'E': return 0xE;
case 'F': return 0xF;
}
return 0x0;
}
DW GetValueDW (char* pszValue)
{
DW dw = 0x00000000;
//comprobar cadena
if (!pszValue || strlen (pszValue)<8) return dw;
//meter en el dword cada char de la cadena
for (short int i=0; i<8; i++)
dw = dw | (0x00000000 | __CHAR2BYTE (*(pszValue+i))<< (28-(i*4)));
return dw;
}
Éste código fue enviaro por CordayUK.
Si quieres enviar tu propio código puedes hacerlo a eth_cotw@lycos.es
Yo propondria un par de mejoras.
Que la función __CHAR2DW devuelva un DW, para evitarnos después la conversión (eso de hacer un | 0x00000000).
También almacenar la longitud de la cadena para poder convertir cadenas más cortas. Por ejemplo, poder escribir en el archivo "AABB" en vez de "0000AABB", si queremos guardar el numero 0x0000AABB
un saludo
typedef unsigned long DW; //es lo mismo que el DWORD definido en windows.h
inline DW __CHAR2DW (unsigned char c)
{
switch (c)
{
case '0': return 0x0;
case '1': return 0x1;
case '2': return 0x2;
case '3': return 0x3;
case '4': return 0x4;
case '5': return 0x5;
case '6': return 0x6;
case '7': return 0x7;
case '8': return 0x8;
case '9': return 0x9;
case 'A': return 0xA;
case 'B': return 0xB;
case 'C': return 0xC;
case 'D': return 0xD;
case 'E': return 0xE;
case 'F': return 0xF;
}
return 0x0;
}
DW GetValueDW (char* pszValue)
{
DW dw = 0x00000000;
int num;
//comprobar cadena
if (!pszValue || (num=strlen (pszValue))>8) return dw;
//meter en el dword cada char de la cadena
for (int i=0; i<num; i++)
dw = dw | __CHAR2DW(pszValue[i]) << ((num-i-1)*4);
return dw;
}
El uso de los índices dentro del bucle se puede simplificar un poco:
for (int i = 0; i<num; ++i)
dw = (dw << 4) | __CHAR2DW(pszValue[i]);
Ahá! , en cierto modo, es más lógico ir desplazando el resultado como FIFO y meter el siguiente dígito siempre en la 1º posición. Si es que a veces nos complicamos la vida.... :)
un saludo
Bueno, pues aquí está mi version de la rutina SUPER-OPTIMIZADA :D
// Aquí está la clave :-D
#define __CHAR2BYTE(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : (c) - 55 )
DWORD GetValueDW (char* pszValue)
{
DWORD dw = 0x00000000;
if (!pszValue || strlen (pszValue)<8) return dw;
// Bucle desenrrollado
dw = dw | __CHAR2BYTE (*(pszValue))<< (28);
dw = dw | __CHAR2BYTE (*(pszValue+1))<< (24);
dw = dw | __CHAR2BYTE (*(pszValue+2))<< (20);
dw = dw | __CHAR2BYTE (*(pszValue+3))<< (16);
dw = dw | __CHAR2BYTE (*(pszValue+4))<< (12);
dw = dw | __CHAR2BYTE (*(pszValue+5))<< (8);
dw = dw | __CHAR2BYTE (*(pszValue+6))<< (4);
dw = dw | __CHAR2BYTE (*(pszValue+7));
return dw;
}
¡A ver que tal os parece!
Todavia se puede optimizar más :ojo: :
#define __CHAR2BYTE(c) ((c) > '9' ? (c) - 'A' : (c) - '0' )
un saludo
Joer fiero, no me has dejado ni editar el post en un momento....XD
¿Pues quedaría algo así, no?
// Mega optimized XD
#define __CHAR2BYTE(c) ((c) > '9' ? (c) - 'A' : (c) - '0' )
DWORD GetValueDW (char* pszValue)
{
DWORD dw = 0x00000000;
if (!pszValue || strlen (pszValue)<8) return dw;
dw = (dw << 4) | __CHAR2BYTE (pszValue[0]);
dw = (dw << 4) | __CHAR2BYTE (pszValue[1]);
dw = (dw << 4) | __CHAR2BYTE (pszValue[2]);
dw = (dw << 4) | __CHAR2BYTE (pszValue[3]);
dw = (dw << 4) | __CHAR2BYTE (pszValue[4]);
dw = (dw << 4) | __CHAR2BYTE (pszValue[5]);
dw = (dw << 4) | __CHAR2BYTE (pszValue[6]);
dw = (dw << 4) | __CHAR2BYTE (pszValue[7]);
return dw;
}
ups, perdón ZaelSiuS, mi - 'A' está mal, tu -55 primero está bien
Donde os mando las mujeres y el vino?????? XDDDDD
muchas gracias, me mola la optimizacion. Ya sabia yo que se podia hacer mejor!!! thx
Y ya para rematar el COTW me he marcado un pequeño programa que testea las dos funciones(original y optimizada).
Lo podeis bajar de aquí:
http://www.alu.ua.es/j/jgf8/files/download...d/getdwtest.zipA mí me saca un 520% de rendimiento más la funcion optimizada( en un Athlon 1600+).
El código de test es casi todo de un artículo de Game Programming Gems 2.
¡Que aproveche!
No rularia mas rapido con una tabla asi ->
char tabla[256];
tabla['1'] = 1;
tabla['2'] = 2;
..
..
tabla['A'] = 10;
desperdicias memoria pero mola XD.
Despues de hacer eso usar
#define __CHAR2BYTE© tabla[c];
es una idea, quizas sea mas rapido hacer lo q haceis vosotros q usar la memoria para ello.
NOTA: las funciones q proponeis NO hacen los mismo q la de corday...
EDIT: error, mire el codigo de fiero del numero de caracteres exacto 0:DDD, lo siento XD
saludos
Ejem, he mirado el codigo para probar la rapidez y no seria mejor tener:
__int64 cuentatiempo(void (*fn)(dword )); ?
el programa de contar el tiempo si q lo optimizaba yo XDD
saludos
Je, je, como nos picamos. :D
Ethernet, lo de las tablas está bien para CPUs sin problemas de caché y todas esas cosistas.
"Ofuscando" un poco el código se evita además las rotaciones:
unsigned char odd[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
unsigned char even[] = {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
0x80, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0};
#define __CHAR_ODD(c) ( (odd - '0')[c])
#define __CHAR_EVEN(c) ( (even -'0')[c])
DWORD GetValueDW(char * pszValue)
{
DWORD dw = 0x00000000;
if ( !pszValue || strlen(pszValue) < 8) return dw;
( (char *)(&dw) )[3] = __CHAR_EVEN(pszValue[0]) | __CHAR_ODD(pszValue[1]);
( (char *)(&dw) )[2] = __CHAR_EVEN(pszValue[2]) | __CHAR_ODD(pszValue[3]);
( (char *)(&dw) )[1] = __CHAR_EVEN(pszValue[4]) | __CHAR_ODD(pszValue[5]);
( (char *)(&dw) )[0] = __CHAR_EVEN(pszValue[6]) | __CHAR_ODD(pszValue[7]);
return dw;
}
El siguiente paso podría ser crear tablas con WORD y DWORD desplazando un byte cada vez. Pero no merece la pena dedicarle más tiempo, como se suele decir: "hay que preocuparse en optimizar lo que realmente necesite ser optimizado". Aunque dedicarle un rato a pesar en cosas como esta es divertido, ¿verdad?.
Venga, buenas noches
CitarAunque dedicarle un rato a pesar en cosas como esta es divertido, ¿verdad?.
Si, divertido e instructivo, por eso es recomendable empezar a hacer comecocos y cosas así en modo texto, porque se aprende realmente a programar con estas cosas sencillitas...
un saludo
Muy c00l el ecodigo juan, q procesadores tienen problemas de cpu actualmente, en cuales no entran 256 bytes? o menos en el caso de tus tablas.
de todas maneras dudo cual de los metodos es mas rapido, si el de las tablas o el propuesto por zaelsius,
pd: todavia me estoy tirando de los pelos del codigo para probar la velocidad ... los de programming gems no tienen ni zorra de lo q es diseñar o q? XD
saludos
El artículo en cuestión del Programming Gems 2 era sobre el uso de optimizaciones al usar floats y tablas de senos, etc. Ahora no tengo el libro, pero creo que era de un tal Yossarian King, programador de EA en canadá(creo).
Aunque últimamente no me gustan mucho la política de videojuegos de EA...
Hola. Creo que el problema de convertir un string que representa un valor
hexadecimal a su correspondiente valor numérico ha quedado bastante claro en el foro, de todas formas aquí os envio mi pequeña aportación que supongo sería útil.
He partido de la idea de la composición numérica de base-n, basada en la
sucesión: c1*b^0 + c2*b^2 + .... + ci*b^(i-1)
Luego he creado dos versiones de la función, una recursiva que es más lenta pero mas reducida y otra no recursiva. El código es el siguiente:
#include <iostream.h>
#include <string.h>
#define __CHAR2BYTE(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : (c) - 55)
typedef unsigned long DWORD;
DWORD power16(int i){
if (i==0) return 1;
return 16*power16(i-1);
}
DWORD recursiva(char *str, int i, int c = 0){
DWORD hex_val = __CHAR2BYTE(str[c]);
if (i==1) return hex_val;
return hex_val*power16(i-1)+recursiva(str,i-1,c+1);
}
DWORD nopower16(int i){
DWORD power = 1;
for (int n=0;n<i;n++)
power*=16;
return power;
}
DWORD norecursiva(char *str,int i){
DWORD hex_delta = 0,hex_val;
for (int n=i-1;n>=0;n--){
hex_val = __CHAR2BYTE(str[n]);
hex_delta+=hex_val*nopower16(i-n-1);
}
return hex_delta;
}
int main(int argc, char* argv[])
{
cout << "Valor decimal(R):" << recursiva(argv[1],strlen(argv[1]));
cout << "nValor decimal(NR):" << norecursiva(argv[1],strlen(argv[1]));
return 0;
}
Curiosa forma de presentarse ;) Bienvenido seas XD
Cita de: "Interlink"Gracias Mars! :ojo:
Cuando te he saludado aún no habías puesto tu código. Antes me caías bien, ahora me caes mejor XD
Alguien que no teme a la recursividad es un tío serio. Aunque creo que con algún problema con ¿el navegador? a la hora de poner posts :)
Mars, no conocía muy bien el editor! :I
Esta versión recursiva es más optimizada aún ya que los desplazamientos
consumen menos ciclos de CPU.
DWORD recursiva(char *str, int i){
if (i==1) return __CHAR2BYTE(str[i-1]);
return (__CHAR2BYTE(str[i-1]) | recursiva(str,i-1)<<4);
}
Un saludo
COOL !