En este post de programación en ANSI C te mostraré 3 conversiones entre bases numéricas: de decimal a binario, a octal y a hexadecimal. La diferencia con mis otros posts es que en este caso vamos a convertir también la parte fraccionaria, decimal o como le llames a lo que va después del punto decimal.
Entonces vamos a convertir entre binario y decimal con punto decimal, además de convertir a octal, hexadecimal y a todas las otras bases.
Así que al final, con el código que te mostraré, podrás convertir números en base 10 con parte fraccionaria a cualquier base. Por ejemplo, convertir 3722.24
base 10 a E8A.3D70
base 16.
Te repito que la principal diferencia es que ahora este conversor en C soporta conversiones con punto decimal, cosa que me habían solicitado varios usuarios en mis otros posts.
Explicación del algoritmo
Para entender cómo funciona este algoritmo debemos entender la forma manual de hacer la conversión. Para este ejemplo convertiré el número a hexadecimal (pero esto que te muestro igual funciona con base 2 y base 8), así que tenemos 3722.24
como entrada.
Presta atención en que la base es 16, y los dígitos de la misma son 0123456789ABCDEF
.
Lo primero que hacemos es separar la parte entera y la parte fraccionaria, para ello podemos usar modf. Ahora trabajamos con las partes por separado.
Parte entera
En el caso de la parte entera vamos dividiendo entre 16 y obteniendo el residuo de la división. Cada valor de residuo irá conformando la cadena hexadecimal. El proceso va así:
3722 % 16 = 10. Se concatena AL INICIO el dígito A. Ahora la parte entera es 232
232 % 16 = 8. Se concatena AL INICIO el dígito 8. Ahora la parte entera es 14
14 % 16 = 14. Se concatena AL INICIO el dígito E. Ahora la parte entera es 0
Y hasta este momento tenemos la parte entera ya en hexadecimal que equivale a E8A
. Fíjate en que el proceso se detiene cuando la parte entera es 0.
Parte fraccionaria
En este caso vamos a multiplicar la parte fraccionaria por 16 y tomar el valor entero del resultado para agregarlo al final de la cadena hexadecimal.
Ahora la parte fraccionaria igualmente se toma del resultado anterior, y se repite el proceso hasta que el sobrante sea 0. El proceso es así:
0.240000 * 16 = 3.840000. Sobran 3.000000. El dígito es 3. Ahora la parte fraccionaria es 0.840000
0.840000 * 16 = 13.440000. Sobran 13.000000. El dígito es D. Ahora la parte fraccionaria es 0.440000
0.440000 * 16 = 7.040000. Sobran 7.000000. El dígito es 7. Ahora la parte fraccionaria es 0.040000
0.040000 * 16 = 0.640000. Sobran 0.000000. El dígito es 0. Ahora la parte fraccionaria es 0.640000
Y la parte fraccionaria como hexadecimal es 3D70
. Por lo que el número completo ya convertido es: E8A.3D70
.
Esto mismo que aplicamos aquí se va a aplicar para la conversión de decimal a octal y decimal a binario con parte fraccionaria, solo vamos a cambiar la base y los dígitos que la conforman.
Función general para la conversión
Anteriormente había creado una función para cada conversión, pero me di cuenta de que se puede crear una función que proporciona un método de conversión desde decimal a cualquier otra base.
Sin más preámbulo, te la muestro. En los comentarios se explica el funcionamiento:
/**
* Convierte un número decimal a cualquier base
* @param numeroDecimal El número en base 10
* @param cadenaResultado Cadena en donde se almacenará el resultado, pues esta función no devuelve nada, solo coloca el resultado en la cadena indicada
* @param base La base a la que se desea convertir el número. Por ejemplo, para convertir a hexadecimal, la base es 16
* @param digitos Los dígitos que conforman la base a la que se convierte el número. Por ejemplo, para la base 16 son 0123456798ABCDEF
* @author Parzibyte https://parzibyte.me/blog
*/
void decimalACualquierBase(double numeroDecimal, char cadenaResultado[MAXIMA_LONGITUD_CADENA], const int base, const char digitos[])
{
/*
* Separar fracción y entero
* */
double parteEnteraDouble; // Temporal para modf
double parteFraccionaria;
parteFraccionaria = modf(numeroDecimal, &parteEnteraDouble);
int parteEntera = (int)parteEnteraDouble;
/*
* Declarar cadenas
* */
char cadenaParteEntera[MAXIMA_LONGITUD_CADENA] = "";
char cadenaParteFraccionaria[MAXIMA_LONGITUD_CADENA] = "";
// Realizar la conversión de la parte entera
while (parteEntera > 0)
{
int residuo = parteEntera % base;
char digito = digitos[residuo];
concatenarCharACadena(digito, cadenaParteEntera);
parteEntera /= base;
}
// Invertimos la cadena
invertirCadena(cadenaParteEntera);
// Realizar conversión de la parte fraccionaria
double sobrante;
do
{
double resultado = parteFraccionaria * base;
parteFraccionaria = modf(resultado, &sobrante);
char digito = digitos[(int)sobrante];
concatenarCharACadena(digito, cadenaParteFraccionaria);
} while (sobrante != 0);
// Concatenar finalmente la parte entera y fraccionaria en el resultado
strcpy(cadenaResultado, ""); // Limpiar cadena
strcat(cadenaResultado, cadenaParteEntera);
strcat(cadenaResultado, ".");
strcat(cadenaResultado, cadenaParteFraccionaria);
}
Los métodos que no son para el cálculo son para trabajar con cadenas o para invertirlas.
En este caso esta función soporta conversión de números con fracción desde la base 10 a binario, octal y hexadecimal. No devuelve nada, pues coloca el resultado en la cadena que se proporciona al llamarla. Ahora es momento de ver su modo de uso.
Antes que nada debemos declarar el número que se va a convertir y la cadena en donde se almacenará el resultado de la conversión:
// En dónde almacenar el resultado, debe ser una cadena
char resultado[MAXIMA_LONGITUD_CADENA] = "";
// Solicitar número al usuario;
double decimal;
printf("Ingresa el decimal:\n");
scanf("%lf", &decimal);
Obviamente el número puede venir de cualquier lugar o puedes declararlo tú mismo.
Decimal a hexadecimal
Comenzando con el caso del ejemplo, para hacer la conversión invocamos a la función que la base es 16 con los dígitos adecuados:
// Decimal a hexadecimal
decimalACualquierBase(decimal, resultado, 16, "0123456789ABCDEF");
printf("Resultado de convertir el decimal %lf a hexadecimal: %s\n", decimal, resultado);
Decimal a octal
Lo mismo que anteriormente, solo que ahora los dígitos van del 0 al 7 y la base es 8:
// Decimal a octal
decimalACualquierBase(decimal, resultado, 8, "01234567");
printf("Resultado de convertir el decimal %lf a octal: %s\n", decimal, resultado)
Decimal a binario
Para terminar el ejemplo, también podemos convertir de decimal a binario:
// Decimal a binario
decimalACualquierBase(decimal, resultado, 2, "01");
printf("Resultado de convertir el decimal %lf a binario: %s\n", decimal, resultado);
De hecho con esta función podemos convertir a cualquier otra base siempre y cuando indiquemos los dígitos.
Poniendo todo junto
Es momento de ver el código en lenguaje C completo, así como el método main
. Todo queda así:
/*
https://parzibyte.me/blog
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#define MAXIMA_LONGITUD_CADENA 100
void invertirCadena(char cadena[])
{
int longitud = strlen(cadena);
int i;
for (i = 0; i < strlen(cadena) / 2; i++)
{
char temporal = cadena[i];
cadena[i] = cadena[longitud - i - 1];
cadena[longitud - i - 1] = temporal;
}
}
void concatenarCharACadena(char c, char *cadena)
{
char cadenaTemporal[2];
cadenaTemporal[0] = c;
cadenaTemporal[1] = '\0';
strcat(cadena, cadenaTemporal);
}
/**
* Convierte un número decimal a cualquier base
* @param numeroDecimal El número en base 10
* @param cadenaResultado Cadena en donde se almacenará el resultado, pues esta función no devuelve nada, solo coloca el resultado en la cadena indicada
* @param base La base a la que se desea convertir el número. Por ejemplo, para convertir a hexadecimal, la base es 16
* @param digitos Los dígitos que conforman la base a la que se convierte el número. Por ejemplo, para la base 16 son 0123456798ABCDEF
* @author Parzibyte https://parzibyte.me/blog
*/
void decimalACualquierBase(double numeroDecimal, char cadenaResultado[MAXIMA_LONGITUD_CADENA], const int base, const char digitos[])
{
/*
* Separar fracción y entero
* */
double parteEnteraDouble; // Temporal para modf
double parteFraccionaria;
parteFraccionaria = modf(numeroDecimal, &parteEnteraDouble);
int parteEntera = (int)parteEnteraDouble;
/*
* Declarar cadenas
* */
char cadenaParteEntera[MAXIMA_LONGITUD_CADENA] = "";
char cadenaParteFraccionaria[MAXIMA_LONGITUD_CADENA] = "";
// Realizar la conversión de la parte entera
while (parteEntera > 0)
{
int residuo = parteEntera % base;
char digito = digitos[residuo];
concatenarCharACadena(digito, cadenaParteEntera);
parteEntera /= base;
}
// Invertimos la cadena
invertirCadena(cadenaParteEntera);
// Realizar conversión de la parte fraccionaria
double sobrante;
do
{
double resultado = parteFraccionaria * base;
parteFraccionaria = modf(resultado, &sobrante);
char digito = digitos[(int)sobrante];
concatenarCharACadena(digito, cadenaParteFraccionaria);
} while (sobrante != 0);
// Concatenar finalmente la parte entera y fraccionaria en el resultado
strcpy(cadenaResultado, ""); // Limpiar cadena
strcat(cadenaResultado, cadenaParteEntera);
strcat(cadenaResultado, ".");
strcat(cadenaResultado, cadenaParteFraccionaria);
}
int main(int argc, char const *argv[])
{
// En dónde almacenar el resultado, debe ser una cadena
char resultado[MAXIMA_LONGITUD_CADENA] = "";
// Solicitar número al usuario;
double decimal;
printf("Ingresa el decimal:\n");
scanf("%lf", &decimal);
// Decimal a hexadecimal
decimalACualquierBase(decimal, resultado, 16, "0123456789ABCDEF");
printf("Resultado de convertir el decimal %lf a hexadecimal: %s\n", decimal, resultado);
// Decimal a octal
decimalACualquierBase(decimal, resultado, 8, "01234567");
printf("Resultado de convertir el decimal %lf a octal: %s\n", decimal, resultado);
// Decimal a binario
decimalACualquierBase(decimal, resultado, 2, "01");
printf("Resultado de convertir el decimal %lf a binario: %s\n", decimal, resultado);
return 0;
}
Nota: he probado con otras calculadoras y en ocasiones el método presentado aquí no es tan preciso para la parte fraccionaria. Esto es porque me basé en el método manual para la conversión. Recuerda que igualmente eres libre de mejorar el código.
Te dejo más entradas sobre C en mi blog.