viernes, 22 de julio de 2011

Kernighan_Ritchie_2.3 (Convertir de Hexadecimal a Decimal en C)

___________________________________________________________________________________
2.3 Escriba la función htoi(s), que convierte una cadena de dígitos hexadecimales (incluyendo 0x ó 0X en forma optativa) en su valor entero equivalente. Los dígitos permitidos son del 0 al 9, de la a a la f, y de la A a la F.
___________________________________________________________________________________
Solución: Este programa convierte de hexadecimal a decimal. El programa está bien comentado y creo que es fácil de seguir y entender. Es posible realizar éste problema de otras maneras, ésta es una de ellas. El usuario puede introducir los numeros 0, 1, ....9 y las literales A, a, B, b,.....F, f. El uso de minúsculas hace el programa más largo. Cualquier otro dígito envía un mensaje de error. Tal vez le convenga leer la entrada Los números hexadecimales. Una tabla de equivalencias entre números binarios, octales, decimales y hexadecimales, aparece en Ésta página.



/*Este programa convierte de hexadecimal
   a decimal */

 #include <stdio.h>

 #define Tamano 35
 // La variable Tamano se fija en 35
 // porque los enteros de dos bytes
 // ocupan 32 bits y la ultima entrada
 // de una cadena en un arreglo es \n

 /*Prototipo de funcion Recibe*/
 /* Esta funcion recibe la cadena
    pero no verifica que sea valida */

 void Recibe( int a[]);

 /*Prototipo de funcion Imprime*/
 /* Si la cadena es valida se calcula
 e imprime su equivalente decimal, si no
 se manda un mensaje al usuario y se termina
 el programa */

 void Imprime( int b[]);

 /*///////////////////////////////
 // MAIN
 ////////////////////////////////*/ 

 int main()

 {       // Abre main 

 int arreglo[Tamano];
 // En este arreglo se recibiran los valores
 // de la cadena hexadecimal
 int evaluar;
 // La variable evaluar lleva el control
 // de la legalidad de la cadena, es decir
 // se introducen digitos validos como e, 1
 // o se introducen valores invalidos como
 // s, z

 // Un  mensaje que avisa al usuario que
 // hace el programa
 printf("\nEste programa recibe un numero "     
        "hexadecimal y lo convierte a decimal.\n");

 // Se invoca a la funcion Recibe y se le envia
 // la direccion de la primera posicion de arreglo

 Recibe(arreglo);

 // Se calcula e imprime el valor decimal, de 
 // nuevo por referencia 

 Imprime(arreglo);

 return 0;

 }       // Cierra main

 /*////////////////////////////////////
 // FUNCION RECIBE
 ////////////////////////////////////*/

 void Recibe( int a[])

 {  // Abre Recibe 

 /* En el arreglo a se guardaran las 
   entradas de la cadena*/

 int i = 0;
 /* La variable i sirve como contador
    en varios ciclos */

 for( i = 0; i < Tamano; i++ )
 a[i] = ' ';
 // Las entradas se inicializan a ' ' 

 printf("\nIntroduzca un numero hexadecimal: \n");
 /*El siguiente while recibe la cadena y la
   asigna al arreglo en una sola linea.
   Observe que al final hay un ;  */

 i = 0;
 // Se pone i = 0

 while ( (a[i++] = getchar()) != '\n' && Tamano > i );
 printf("\n");

 }  // Cierra Recibe 

 /*/////////////////////////////////////
 // FUNCION IMPRIME
 /////////////////////////////////////*/

 void Imprime( int b[] )

 {  // Abre Imprime

 int i;
 int potencia = 1;
 // La variable potencia lleva el valor de la
 // potencia de 16 por la cual se va a multiplicar
 // el digito almacenado dependiendo de su posicion
 int decimal = 0;
 // El equivalente decimal de la cadena hexadecimal
 // se inicia a 0
 int inicio;
 // La variable inicio se encarga de registrar
 // en donde empieza el primer digito de la cadena.
 // Esta cadena debe leerse de izquierda a derecha.

 // Este ciclo for busca de atras para 
 // adelante el primer digito distinto
 // de ' ' en el arreglo. A partir de
 // ahi se empieza a evaluar el numero
 // hexadecimal

 for ( i = Tamano - 1; i >= 0; i-- )

 {  // Abre for
 if ( ' ' != b[i] )
 {  // abre if  
 inicio = i;
 break; // sale de for
 }   // Cierra if
 }  // Cierra for

 // Este ciclo for evalua cada una de las
 // entradas del arreglo que fueron introducidas 
 // por el usuario, las multiplica por la potencia 
 // de 16 correspondiente a su posicion y los
 // productos los va sumando. La suma total sera
 // el valor decimal del numero. 

 int valido = 1;
 // Si el hexadecimal recibido no es valido, no 
 // se puede calcular su valor decimal. Esta 
 // variable controla eso. En principio se establece
 // en 1 o cierto, ya que se asume que el usuario 
 // introdujo un numero valido

  for ( i = inicio; i >= 0; i-- )
 {  // Abre for
 switch( b[i] )
 {  // Abre switch
 case '0':
 decimal += 0*potencia;
 potencia *= 16;
 break;
 case '1':
 decimal += 1*potencia;
 potencia *= 16;
 break;
 case '2':
 decimal += 2*potencia;
 potencia *= 16;
 break;
 case '3':
 decimal += 3*potencia;
 potencia *= 16;
 break;
 case '4':
 decimal += 4*potencia;
 potencia *= 16;
 break;
 case '5':
 decimal += 5*potencia;
 potencia *= 16;
 break;
 case '6':
 decimal += 6*potencia;
 potencia *= 16;
 break;
 case '7':
 decimal += 7*potencia;
 potencia *= 16;
 break;
 case '8':
 decimal += 8*potencia;
 potencia *= 16;
 break;
 case '9':
 decimal += 9*potencia;
 potencia *= 16;
 break;
 case 'A':
 decimal +=  10*potencia;
 potencia *= 16;
 break;
 case 'a':
 decimal +=  10*potencia;
 potencia *= 16;
 break;
 case 'B':
 decimal +=  11*potencia;
 potencia *= 16;
 break;
 case 'b':
 decimal +=  11*potencia;
 potencia *= 16;
 break;
 case 'C':
 decimal +=  12*potencia;
 potencia *= 16;
 break;
 case 'c':
 decimal +=  12*potencia;
 potencia *= 16;
 break;
 case 'D':
 decimal +=  13*potencia;
 potencia *= 16;
 break;
 case 'd':
 decimal +=  13*potencia;
 potencia *= 16;
 break;
 case 'E':
 decimal +=  14*potencia;
 potencia *= 16;
 break;
 case 'e':
 decimal +=  14*potencia;
 potencia *= 16;
 break;
 case 'F':
 decimal +=  15*potencia;
 potencia *= 16;
 break;
 case 'f':
 decimal +=  15*potencia;
 potencia *= 16;
 break;
 default:
 if ( '\n' != b[i])
 {  // Abre if
 printf("\nERROR. LA CADENA NO ES VALIDA!\n");
 valido = 0;  // El hexadecimal recibido no 
              // es valido
 printf("\nEste caracter no es valido: ");
 putchar(b[i]);
 printf("\n");
 } // Cierra if
 break;
 }   // Cierra switch
 }  // Cierra for  

 if ( 1 == valido )
 printf("\nEl valor decimal es: %d\n", decimal);
 else
 printf("\nEl numero introducido no es valido.\n");

 }  // Cierra Imprime
__________________________________________________________________________________________
Esta entrada es parte de los problemas resueltos del libro El Lenguaje de Programación C de B. Kernighan y D. Ritchie
Entrada Anterior
Entrada Siguiente

__________________________________________________________________________________________

17 comentarios:

  1. Hola. El programa está bien. Lo he revisado y no hay ningún problema. Si dices que no corre debes estar compilando mal o copiando mal. Copia y pega el programa, sólo ten cuidado copiarlo y pegarlo completo. Yo compilo en linux con cc o gcc y funciona.

    ResponderEliminar
  2. Hola, una consulta, porque cuando escribo un numero de 8 o mas digitos el resultado no es el correcto.. ejemplo ffffffff(8) es igual a -1
    Saludos..

    ResponderEliminar
  3. No entendi esto que escribiste..

    // La variable Tamano se fija en 35
    // porque los enteros de dos bytes
    // ocupan 32 bits y la ultima entrada
    // de una cadena en un arreglo es \n

    No sera 16 bits¿?

    ResponderEliminar
    Respuestas
    1. ¡Hola, Roberto! Tienes razon. Es 16 bits. Por otro lado, la razòn por la que si metes un nùmero muy grande, obtienes nùmeros negativos, es porque la variable decimal se declara del tipo int. Lo que pasa es que el tipo de datos es superado. Si quieres nùmeros màs grandes puedes usar un tipo double, por ejemplo. Checa Esta pàgina si te interesa un poco màs èsto.
      Muchos saludos.

      Eliminar
  4. hola, disculpa, estoy tratando de escribir este programa y lo que quiero hacer es multiplicar el elemento de un arreglo por un entero, osea:

    pot=2
    num[i]*pot;

    y lo que hace es multiplicarme el valor ascii del caracter en esa posicion del arreglo..
    no se si me explique bien..
    ejemplo, si en num[i] esta guardado el numero '2' lo que hace es 50*2, 2 en ASCII es 50..

    ResponderEliminar
    Respuestas
    1. ¡Hola, Roberto!, perdón por contestarte hasta ahora, he estado un poco ocupado. ¿Estarás guardando tu variable i como tipo char?, incluso más bien parece que simplemente no la estás declarando como un tipo de datos, sino que el compilador la toma como si fuera un 'i'. Debes declararla como tipo int.
      Saludos.

      Eliminar
    2. Hola, no lo declaraba como tipo INT, igualmente lo arregle realizando pot*(num[i]-'0') pero sinceramente no se porque funciona.. porque segun el libro al realizar num[i]-'0' esa expresion lo que hace es devolver el valor numerico del caracter almacenado..

      Eliminar
    3. Lo que pasa es que si restas '0', estás restando el valor ASCII de 0. Lo de num[i] me sigue pareciendo extraño.

      Eliminar
  5. Mira esta es mi version: http://codepad.org/SkJUjRjw

    no use switch, break, case, porque no llegue hasta esa parte..

    Y ahora si a num[j] NO le resto '0' anda bien, osea ahora anda bien de las dos formas, antes si multiplicaba num[j] por algo, me multiplicaba el valor en ASCII del caracter en esa direccion.. capas tenia algo mal porque modifique el codigo..

    Creo que el codigo se podria achicar mas en la parte de las letras, pero no se me ocurre como.. o por lo menos con lo que vi por ahora en el libro..

    ResponderEliminar
    Respuestas
    1. Está muy compacto tu código. Me gustó mucho. Pongo el enlace: programa

      Eliminar
  6. Yo aqui tengo mi versión de htoi, pensada como una función fuera del main:

    int htoi(const char s[]){
    int i,h;
    h = i = 0;
    if (s[i]=='0'){ /* Verifica si es un hexadecimal precedido por 0x o 0X*/
    ++i;
    if (s[i]=='x'||s[i]=='X')
    ++i;
    }
    for (i; isHexa(s[i]); ++i)
    h = (h * 16) + valHexa(s[i]);
    return (h); /* Retorna 0 en caso de que el numero sea una entrada incorrecta o 0*/
    }

    int isHexa(int c){ /*verifica si es un digito hexadecimal*/
    return (isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') );
    }

    int valHexa (int c){ /*convierte un caracter hexadecimal a su valor decimal*/
    if (isdigit (c))
    c = c - '0';
    else
    c = tolower (c) - 'a' + 10;
    return (c);
    }

    Utlizo la biblioteca para utilizar las funcion isDigit(int) que detecta si un caracter es un digito entre 0 y 9 y tolower(char) que convierte un caracter mayuscula a su equivalente en minuscula.
    Lo unico que destaco es que la funcion devuelve 0 en caso de error y que convierte a hexadecimal cadenas que podrian ser numeros octales. Ej: "0562" ya que en el libro de C los numeros octales empiezan con un 0.
    para probarlo hay que realizar una funcion main que invoque a la funcion htoi y pasarle una cadena de caracteres que contenga un numero hexadecimal.

    ResponderEliminar
  7. aqui dejo la funcion junto a un programa que la utilice: http://codepad.org/5dADTDjs

    ResponderEliminar
  8. Disculpen habia un error en cuanto a la deteccion de un error.
    Aqui te dejo una nueva pagina en la uqe esta el codigo de htoi: http://codepad.org/wgBJLtYe

    ResponderEliminar
  9. ¡Hola, Martín! Muchas gracias por tu aporte. Te agradezco la gentileza de compartir el código. Acabo de revisarlo y me parece muy bueno. El punto aquí es que yo no conocía la función isdigit, que, según me cuentas, se incluye en el encabezado ctype. En buena medida trato de utilizar para resolver estos problemas, los recursos que se conocen hasta ésta altura en el libro de Kernighan-Ritchie, porque luego me ha pasado que algunas personas dicen que utilizo cosas que no se han visto, y de alguna manera es "hacer trampa", Desde luego, creo que hay que utilizar las herramientas ya incluidas en el lenguaje.
    Te agradezco de nuevo, y muchos saludos.

    ResponderEliminar

Related Posts Plugin for WordPress, Blogger...