Conversor de números en Python

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):

Conversor de bases numéricas (binario, octal, decimal y hexadecimal) en Python

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.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *