En este post de programación en Python te mostraré un conversor de bases numéricas. Es decir, un convertidor de números que puede convertir entre binario, octal, decimal y hexadecimal en todas las combinaciones posibles.
Dicho con otras palabras, con este convertidor de números en Python podremos convertir de binario a decimal, binario a octal, octal a hexadecimal y todas las combinaciones que se te ocurran.
También he incluido un archivo de pruebas unitarias para probar que los métodos funcionan, además de un ejemplo de uso de este conversor.
Explicando funcionamiento general
Este programa va a reunir todas las conversiones que veníamos viendo anteriormente, y simplemente va a usarlas para convertir entre cualquier base, tomando atajos. Las conversiones son: de binario a decimal, y viceversa. De octal a decimal y de base 10 a base 8. También de hexadecimal a decimal y del proceso contrario.
Por ejemplo, para convertir de octal a hexadecimal no vamos a hacer la conversión directa, sino que primero vamos a convertir de octal a decimal, y de decimal a hexadecimal. Es decir, la base 10 o decimal será la base común.
De este modo nos ahorramos programar varias funciones, pero ofrecemos la conversión entre cualquier base.
Funciones de conversión
Todas las conversiones de números las tenemos en un archivo que vamos a importar en el archivo principal:
# Función que regresa el verdadero valor hexadecimal.
# Por ejemplo, si recibe un 15 devuelve f, y si recibe un número menor a 10, devuelve el número sin modificarlo
def obtener_caracter_hexadecimal(valor):
# Lo necesitamos como cadena
valor = str(valor)
equivalencias = {
"10": "a",
"11": "b",
"12": "c",
"13": "d",
"14": "e",
"15": "f",
}
if valor in equivalencias:
return equivalencias[valor]
else:
return valor
def decimal_a_hexadecimal(decimal):
hexadecimal = ""
while decimal > 0:
residuo = decimal % 16
verdadero_caracter = obtener_caracter_hexadecimal(residuo)
hexadecimal = verdadero_caracter + hexadecimal
decimal = int(decimal / 16)
return hexadecimal
def obtener_valor_real(caracter_hexadecimal):
equivalencias = {
"f": 15,
"e": 14,
"d": 13,
"c": 12,
"b": 11,
"a": 10,
}
if caracter_hexadecimal in equivalencias:
return equivalencias[caracter_hexadecimal]
else:
return int(caracter_hexadecimal)
def hexadecimal_a_decimal(hexadecimal):
# Convertir a minúsculas para hacer las cosas más simples
hexadecimal = hexadecimal.lower()
# La debemos recorrer del final al principio, así que la invertimos
hexadecimal = hexadecimal[::-1]
decimal = 0
posicion = 0
for digito in hexadecimal:
# Necesitamos que nos dé un 10 para la A, un 9 para el 9, un 11 para la B, etcétera
valor = obtener_valor_real(digito)
elevado = 16 ** posicion
equivalencia = elevado * valor
decimal += equivalencia
posicion += 1
return decimal
def decimal_a_octal(decimal):
octal = ""
while decimal > 0:
residuo = decimal % 8
octal = str(residuo) + octal
decimal = int(decimal / 8)
return octal
def octal_a_decimal(octal):
decimal = 0
posicion = 0
# Invertir octal, porque debemos recorrerlo de derecha a izquierda
# pero for in empieza de izquierda a derecha
octal = octal[::-1]
for digito in octal:
valor_entero = int(digito)
numero_elevado = int(8 ** posicion)
equivalencia = int(numero_elevado * valor_entero)
decimal += equivalencia
posicion += 1
return decimal
def decimal_a_binario(decimal):
if decimal <= 0:
return "0"
# Aquí almacenamos el resultado
binario = ""
# Mientras se pueda dividir...
while decimal > 0:
# Saber si es 1 o 0
residuo = int(decimal % 2)
# E ir dividiendo el decimal
decimal = int(decimal / 2)
# Ir agregando el número (1 o 0) a la izquierda del resultado
binario = str(residuo) + binario
return binario
def binario_a_decimal(binario):
posicion = 0
decimal = 0
# Invertir la cadena porque debemos recorrerla de derecha a izquierda
# https://parzibyte.me/blog/2019/06/26/invertir-cadena-python/
binario = binario[::-1]
for digito in binario:
# Elevar 2 a la posición actual
multiplicador = 2**posicion
decimal += int(digito) * multiplicador
posicion += 1
return decimal
En caso de que no entiendas el algoritmo de alguna función, dirígete al post que he dejado citado anteriormente para cada una de ellas.
Programa principal del convertidor en Python
Como te habrás dado cuenta, la complejidad de este conversor no está en las funciones de conversión, pues esas ya están programadas. Lo que falta programar es la pieza que le va a solicitar al usuario desde cuál base convierte, cuál número convierte y a qué base lo quiere convertir.
Vamos paso por paso, primero veamos la función que solicita los 3 datos mencionados anteriormente y los devuelve como una tupla en caso de que sean válidos:
def solicitar_datos_a_usuario():
bases_soportadas = ["2", "8", "10", "16", ]
base_origen = input("""
2 - Binario
8 - Octal
10 - Decimal
16 - Hexadecimal
Elige la base desde donde conviertes: [2, 8, 10, 16]: """)
if base_origen not in bases_soportadas:
print("La base que ingresaste no está soportada")
return
numero = input(
f"Ok, vas a convertir desde la base {base_origen}. Ingresa el número a convertir: ")
base_destino = input("""
2 - Binario
8 - Octal
10 - Decimal
16 - Hexadecimal
Elige la base a la que conviertes: [2, 8, 10, 16]: """)
if base_destino not in bases_soportadas:
print("La base de destino no está soportada")
return
return (base_origen, numero, base_destino)
Básicamente solicita al usuario la base desde la que se convierte (binario, octal, decimal o hexadecimal), el número que convierte (mismo que debe estar en la base solicitada en el primer paso) y la base a la que quiere convertir.
Fíjate en que la función va a devolver None
en caso de que el usuario no elija una base válida.
Ahora veamos la función que va a convertir el número a decimal, pues recuerda que todos los números serán convertidos a decimal, y desde decimal, a otras bases. Esto con el fin de ahorrarnos funciones. Entonces la función queda así:
def obtener_numero_decimal(base_origen, numero):
if base_origen == "2":
return conversiones.binario_a_decimal(numero)
elif base_origen == "8":
return conversiones.octal_a_decimal(numero)
elif base_origen == "10":
return int(numero)
elif base_origen == "16":
return conversiones.hexadecimal_a_decimal(numero)
Finalmente veamos la función que hace la conversión de acuerdo a la base:
def convertir(numero, base_destino):
if base_destino == "2":
return conversiones.decimal_a_binario(numero)
elif base_destino == "8":
return conversiones.decimal_a_octal(numero)
elif base_destino == "10":
return int(numero)
elif base_destino == "16":
return conversiones.decimal_a_hexadecimal(numero)
Esta función va a devolver la conversión a la base numérica que el usuario haya elegido.
Poniendo todo junto
Así que el programa principal queda de la siguiente manera:
import conversiones
# Si todo va bien, regresa una tupla con la base de origen, el número a convertir y la base de destino
def solicitar_datos_a_usuario():
bases_soportadas = ["2", "8", "10", "16", ]
base_origen = input("""
2 - Binario
8 - Octal
10 - Decimal
16 - Hexadecimal
Elige la base desde donde conviertes: [2, 8, 10, 16]: """)
if base_origen not in bases_soportadas:
print("La base que ingresaste no está soportada")
return
numero = input(
f"Ok, vas a convertir desde la base {base_origen}. Ingresa el número a convertir: ")
base_destino = input("""
2 - Binario
8 - Octal
10 - Decimal
16 - Hexadecimal
Elige la base a la que conviertes: [2, 8, 10, 16]: """)
if base_destino not in bases_soportadas:
print("La base de destino no está soportada")
return
return (base_origen, numero, base_destino)
def obtener_numero_decimal(base_origen, numero):
if base_origen == "2":
return conversiones.binario_a_decimal(numero)
elif base_origen == "8":
return conversiones.octal_a_decimal(numero)
elif base_origen == "10":
return int(numero)
elif base_origen == "16":
return conversiones.hexadecimal_a_decimal(numero)
def convertir(numero, base_destino):
if base_destino == "2":
return conversiones.decimal_a_binario(numero)
elif base_destino == "8":
return conversiones.decimal_a_octal(numero)
elif base_destino == "10":
return int(numero)
elif base_destino == "16":
return conversiones.decimal_a_hexadecimal(numero)
if __name__ == '__main__':
datos = solicitar_datos_a_usuario()
# Comprobamos si los datos son correctos
if datos:
base_origen, numero, base_destino = datos
# Para ahorrarnos código, vamos a convertir el número a decimal (sin importar la base de origen) y luego ese número
# lo convertimos a la base de destino
numero_decimal = obtener_numero_decimal(base_origen, numero)
# Y a ese decimal lo convertimos a la base deseada
resultado = convertir(numero_decimal, base_destino)
print(resultado)
Recuerda que necesitas el archivo de conversiones que ya vimos anteriormente. En mi caso lo he probado con los siguientes tests unitarios:
import conversiones
import conversor
import unittest
class TestConversiones(unittest.TestCase):
def test_binario_decimal(self):
esperado = 7
actual = conversiones.binario_a_decimal("111")
self.assertEqual(actual, esperado)
def test_decimal_binario(self):
esperado = "111"
actual = conversiones.decimal_a_binario(7)
self.assertEqual(actual, esperado)
def test_octal_decimal(self):
esperado = 123
actual = conversiones.octal_a_decimal("173")
self.assertEqual(actual, esperado)
def test_decimal_octal(self):
esperado = "173"
actual = conversiones.decimal_a_octal(123)
self.assertEqual(actual, esperado)
def test_hexadecimal_decimal(self):
esperado = 255
actual = conversiones.hexadecimal_a_decimal("ff")
self.assertEqual(actual, esperado)
def test_decimal_hexadecimal(self):
esperado = "ff"
actual = conversiones.decimal_a_hexadecimal(255)
self.assertEqual(actual, esperado)
def test_obtener_numero_decimal(self):
valores = [
{
"base": "2",
"numero": "111",
"esperado": 7,
},
{
"base": "8",
"numero": "173",
"esperado": 123,
},
{
"base": "16",
"numero": "f",
"esperado": 15,
},
]
for valor in valores:
esperado = valor["esperado"]
actual = conversor.obtener_numero_decimal(
valor["base"], valor["numero"])
self.assertEqual(actual, esperado)
if __name__ == "__main__":
unittest.main()
Además, lo he probado manualmente desde la consola (y también ejecutado los tests):
Funciona perfectamente. En este caso ejemplifiqué las conversiones con datos solicitados por teclado, aunque bien podrías ponerlo en una API, agregar una interfaz gráfica con tkinter o Qt, etcétera.
Te invito a seguir leyendo más sobre Python en mi blog.