Skip to content
IRC-CodingIRC-Coding
Sistemas numéricosBinario HexadecimalBit operacionesConversiónAritmética computacional

Sistemas Numéricos: Binario, Hexadecimal y Operaciones de Bit

Aprende sistemas numéricos: binario, hexadecimal, decimal. Conversiones, operaciones bit, complemento a dos y aritmética computacional con ejemplos.

S

schutzgeist

29 min read
Sistemas Numéricos: Binario, Hexadecimal y Operaciones de Bit

Sistemas de numeración: Binario, Hexadecimal, Decimal y Operaciones con bits

Este artículo es una introducción completa a los sistemas de numeración, incluyendo binario, hexadecimal, decimal, conversiones y operaciones con bits, todo con ejemplos prácticos.

Lo esencial

Las computadoras utilizan sistemas binarios (0 y 1). El hexadecimal actúa como representación compacta para valores binarios. Las operaciones con bits permiten manipulación directa de datos a nivel de bit.

Ruta de aprendizaje

Principiante (Pasos 1-3):

  1. Comprender los fundamentos de los sistemas de numeración (decimal, binario, hexadecimal)
  2. Practicar conversiones simples (decimal ↔ binario ↔ hexadecimal)
  3. Aprender operaciones básicas con bits (AND, OR, XOR)

Intermedio (Pasos 4-6): 4. Entender el complemento a dos y números negativos 5. Aplicaciones prácticas (direcciones IP, colores, direcciones MAC) 6. Operaciones de desplazamiento y manipulación de bits

Avanzado (Pasos 7-10): 7. Temas especializados (BCD, código Gray, CRC) 8. Formato de punto flotante IEEE 754 9. Endianness y orden de bytes en red 10. Optimización de rendimiento con operaciones de bits

💡 Consejo profesional: comienza con los ejercicios interactivos para poner a prueba tu comprensión.

Descripción técnica concisa

Los sistemas de numeración son métodos para representar números usando diferentes bases. Las computadoras utilizan internamente el sistema binario (base 2), mientras que los humanos preferimos el sistema decimal (base 10).

Sistemas de numeración importantes:

Sistema decimal (base 10)

  • Dígitos: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
  • Valores posicionales: 10⁰, 10¹, 10², 10³, …
  • Uso: comunicación humana, matemáticas cotidianas
  • Ejemplo: 123 = 1×10² + 2×10¹ + 3×10⁰

Representación visual:

   1      2      3
  ↓      ↓      ↓
10²    10¹    10⁰
100    10     1
= 100 + 20 + 3 = 123

Sistema binario (base 2)

  • Dígitos: 0, 1
  • Valores posicionales: 2⁰, 2¹, 2², 2³, …
  • Uso: representación interna en computadoras
  • Ejemplo: 1011 = 1×2³ + 0×2² + 1×2¹ + 1×2⁰ = 11

⚠️ Importante: las computadoras trabajan internamente solo con 0 y 1. Todos los datos finalmente se almacenan como números binarios.

Representación visual:

   1      0      1      1
  ↓      ↓      ↓      ↓
2³     2²     2¹     2⁰
8      4      2      1
= 8 + 0 + 2 + 1 = 11

Sistema hexadecimal (base 16)

  • Dígitos: 0-9, A, B, C, D, E, F
  • Valores posicionales: 16⁰, 16¹, 16², 16³, …
  • Uso: representación binaria compacta, colores, direcciones de memoria
  • Ejemplo: A3 = 10×16¹ + 3×16⁰ = 163

💡 Consejo profesional: 1 dígito hexadecimal = 4 dígitos binarios. El hexadecimal es perfecto para representar binario porque es más compacto y legible.

Representación visual:

   A      3
  ↓      ↓
16¹    16⁰
16     1
= 160 + 3 = 163

Sistema octal (base 8)

  • Dígitos: 0, 1, 2, 3, 4, 5, 6, 7
  • Valores posicionales: 8⁰, 8¹, 8², 8³, …
  • Uso: históricamente usado en sistemas Unix
  • Ejemplo: 75 = 7×8¹ + 5×8⁰ = 61

📜 Histórico: el octal fue ampliamente utilizado en los primeros sistemas Unix (por ejemplo, permisos de archivo con chmod 755). Hoy en día el hexadecimal es más común.

Representación visual:

   7      5
  ↓      ↓
8¹     8⁰
8      1
= 56 + 5 = 61

Puntos clave para examen

  • Sistema decimal: base 10, dígitos 0-9, valores posicionales 10ⁿ
  • Sistema binario: base 2, dígitos 0-1, representación en computadoras
  • Sistema hexadecimal: base 16, dígitos 0-9 y A-F, representación compacta
  • Conversión: división/multiplicación por la base, método de valores posicionales
  • Operaciones con bits: AND, OR, XOR, NOT, operaciones de desplazamiento
  • Complemento a dos: números negativos en sistema binario
  • Relevante para exámenes: base de arquitectura de computadoras y programación

Componentes principales

  1. Sistemas de numeración: decimal, binario, hexadecimal, octal
  2. Conversión: entre diferentes bases
  3. Operaciones con bits: AND, OR, XOR, NOT, desplazamiento
  4. Complemento a dos: números negativos
  5. Aritmética de computadora: suma, resta, multiplicación
  6. Tipos de datos: bits, bytes, palabras
  7. Representación de memoria: direcciones hexadecimales
  8. Detección de errores: paridad, sumas de comprobación

Conversiones paso a paso

💡 Consejo profesional: practica estas conversiones regularmente; son fundamentales para entender sistemas de computadoras. Además, aparecen en casi todos los exámenes de certificación técnica.

Nuestro calculador en línea gratuito:

Zahlensystem-Umrechnung

Decimal a binario (división entre 2)

Ejemplo: 13₁₀ → Binario

13 ÷ 2 = 6 resto 1  ← bit menos significativo
 6 ÷ 2 = 3 resto 0
 3 ÷ 2 = 1 resto 1
 1 ÷ 2 = 0 resto 1  ← bit más significativo

Leer de abajo hacia arriba: 1101₂

Binario a decimal (método de valores posicionales)

Ejemplo: 1101₂ → Decimal

1 1 0 1
↓ ↓ ↓ ↓
2³ 2² 2¹ 2⁰
8 4 2 1

= 1×8 + 1×4 + 0×2 + 1×1
= 8 + 4 + 0 + 1
= 13₁₀

Decimal a hexadecimal (división entre 16)

Ejemplo: 255₁₀ → Hex

255 ÷ 16 = 15 resto 15 (F)
 15 ÷ 16 =  0 resto 15 (F)

Leer de abajo hacia arriba: FF₁₆

Hexadecimal a binario (grupos de 4 bits)

Ejemplo: A3₁₆ → Binario

A (10)      3
↓           ↓
1010        0011

Combinado: 10100011₂

Binario a hexadecimal (grupos de 4 bits)

Ejemplo: 10100011₂ → Hex

1010 0011
↓     ↓
A     3

Combinado: A3₁₆

Ejemplos prácticos

1. Conversión entre sistemas de numeración

public class SistemasNumeracion {
    
    // Decimal a binario
    public static String decimalABinario(int decimal) {
        if (decimal == 0) return "0";
        
        StringBuilder binario = new StringBuilder();
        while (decimal > 0) {
            binario.append(decimal % 2);
            decimal /= 2;
        }
        return binario.reverse().toString();
    }
    
    // Binario a decimal
    public static int binarioADecimal(String binario) {
        int decimal = 0;
        int potencia = 0;
        
        for (int i = binario.length() - 1; i >= 0; i--) {
            if (binario.charAt(i) == '1') {
                decimal += Math.pow(2, potencia);
            }
            potencia++;
        }
        return decimal;
    }
    
    // Decimal a hexadecimal
    public static String decimalAHex(int decimal) {
        if (decimal == 0) return "0";
        
        char[] digitos = {'0', '1', '2', '3', '4', '5', '6', '7', 
                         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        StringBuilder hex = new StringBuilder();
        
        while (decimal > 0) {
            int resto = decimal % 16;
            hex.append(digitos[resto]);
            decimal /= 16;
        }
        return hex.reverse().toString();
    }
    
    // Hexadecimal a decimal
    public static int hexADecimal(String hex) {
        int decimal = 0;
        String digitos = "0123456789ABCDEF";
        
        for (int i = 0; i < hex.length(); i++) {
            char caracter = hex.charAt(i);
            int valor = digitos.indexOf(caracter);
            decimal = decimal * 16 + valor;
        }
        return decimal;
    }
    
    // Binario a hexadecimal
    public static String binarioAHex(String binario) {
        // Dividir binario en grupos de 4
        while (binario.length() % 4 != 0) {
            binario = "0" + binario;
        }
        
        StringBuilder hex = new StringBuilder();
        for (int i = 0; i < binario.length(); i += 4) {
            String grupo = binario.substring(i, i + 4);
            int valor = binarioADecimal(grupo);
            hex.append(decimalAHex(valor));
        }
        return hex.toString();
    }
    
    // Hexadecimal a binario
    public static String hexABinario(String hex) {
        String[] mapaHex = {
            "0000", "0001", "0010", "0011",
            "0100", "0101", "0110", "0111",
            "1000", "1001", "1010", "1011",
            "1100", "1101", "1110", "1111"
        };
        
        StringBuilder binario = new StringBuilder();
        for (int i = 0; i < hex.length(); i++) {
            char caracter = hex.charAt(i);
            int valor = "0123456789ABCDEF".indexOf(caracter);
            binario.append(mapaHex[valor]);
        }
        
        // Eliminar ceros iniciales
        while (binario.length() > 1 && binario.charAt(0) == '0') {
            binario.deleteCharAt(0);
        }
        
        return binario.toString();
    }
    
    public static void main(String[] args) {
        int numero = 175;
        
        System.out.println("Número: " + numero);
        System.out.println("Binario: " + decimalABinario(numero));
        System.out.println("Hexadecimal: " + decimalAHex(numero));
        
        String binario = "10101111";
        String hex = "AF";
        
        System.out.println("\nBinario " + binario + " = Decimal " + binarioADecimal(binario));
        System.out.println("Hexadecimal " + hex + " = Decimal " + hexADecimal(hex));
        System.out.println("Binario " + binario + " = Hexadecimal " + binarioAHex(binario));
        System.out.println("Hexadecimal " + hex + " = Binario " + hexABinario(hex));
    }
}

2. Operaciones con bits en diferentes lenguajes

// Java - Operaciones con bits
public class BitOperaciones {
    
    public static void main(String[] args) {
        int a = 12;  // 1100 en binario
        int b = 10;  // 1010 en binario
        
        System.out.println("a = " + a + " (Binario: " + Integer.toBinaryString(a) + ")");
        System.out.println("b = " + b + " (Binario: " + Integer.toBinaryString(b) + ")");
        
        // AND bit a bit (&)
        int and = a & b;  // 1100 & 1010 = 1000 (8)
        System.out.println("a & b = " + and + " (Binario: " + Integer.toBinaryString(and) + ")");
        
        // OR bit a bit (|)
        int or = a | b;   // 1100 | 1010 = 1110 (14)
        System.out.println("a | b = " + or + " (Binario: " + Integer.toBinaryString(or) + ")");
        
        // XOR bit a bit (^)
        int xor = a ^ b;  // 1100 ^ 1010 = 0110 (6)
        System.out.println("a ^ b = " + xor + " (Binario: " + Integer.toBinaryString(xor) + ")");
        
        // NOT bit a bit (~)
        int notA = ~a;   // ~1100 = 0011 (con complemento a dos)
        System.out.println("~a = " + notA + " (Binario: " + Integer.toBinaryString(notA) + ")");
        
        // Desplazamiento a la izquierda (<<)
        int leftShift = a << 2;  // 1100 << 2 = 110000 (48)
        System.out.println("a << 2 = " + leftShift + " (Binario: " + Integer.toBinaryString(leftShift) + ")");
        
        // Desplazamiento a la derecha (>>)
        int rightShift = a >> 1;  // 1100 >> 1 = 0110 (6)
        System.out.println("a >> 1 = " + rightShift + " (Binario: " + Integer.toBinaryString(rightShift) + ")");
        
        // Desplazamiento a la derecha sin signo (>>>)
        int unsignedRightShift = a >>> 1;  // 1100 >>> 1 = 0110 (6)
        System.out.println("a >>> 1 = " + unsignedRightShift + " (Binario: " + Integer.toBinaryString(unsignedRightShift) + ")");
        
        // Aplicaciones prácticas
        aplicacionesPracticas();
    }
    
    private static void aplicacionesPracticas() {
        System.out.println("\n=== Aplicaciones Prácticas ===");
        
        // Verificar si un bit está establecido (¿está el 3er bit activado?)
        int numero = 12;  // 1100
        int posicionBit = 2;
        boolean bitActivado = (numero & (1 << posicionBit)) != 0;
        System.out.println("Bit " + posicionBit + " en " + numero + " activado: " + bitActivado);
        
        // Establecer un bit
        int conBit = numero | (1 << posicionBit);
        System.out.println("Activar bit " + posicionBit + ": " + conBit);
        
        // Limpiar un bit
        int sinBit = numero & ~(1 << posicionBit);
        System.out.println("Desactivar bit " + posicionBit + ": " + sinBit);
        
        // Cambiar un bit (toggle)
        int intercambiado = numero ^ (1 << posicionBit);
        System.out.println("Cambiar bit " + posicionBit + ": " + intercambiado);
        
        // Extraer componentes RGB de un color
        int color = 0xFF6B35;  // Naranja
        int rojo = (color >> 16) & 0xFF;
        int verde = (color >> 8) & 0xFF;
        int azul = color & 0xFF;
        
        System.out.println("\nColor: #" + Integer.toHexString(color).toUpperCase());
        System.out.println("Rojo: " + rojo);
        System.out.println("Verde: " + verde);
        System.out.println("Azul: " + azul);
    }
}

3. Complemento a dos para números negativos

⚠️ Importante: El complemento a dos es el estándar para representar números negativos en computadores modernos. Evita el complemento a uno y la notación de magnitud con signo.

public class ComplementoADos {
    
    // Calcular complemento a dos
    public static String complementoADos(int numero, int bits) {
        if (numero >= 0) {
            return String.format("%" + bits + "s", Integer.toBinaryString(numero)).replace(' ', '0');
        }
        
        // Número negativo: 2^bits + numero
        int positivo = (int) (Math.pow(2, bits) + numero);
        return String.format("%" + bits + "s", Integer.toBinaryString(positivo)).replace(' ', '0');
    }
    
    // Convertir desde complemento a dos a valor decimal
    public static int desdeComplementoADos(String binario) {
        int bits = binario.length();
        
        // Si el bit más alto es 0, es un número positivo
        if (binario.charAt(0) == '0') {
            return Integer.parseInt(binario, 2);
        }
        
        // Número negativo: - (2^bits - valor)
        int valor = Integer.parseInt(binario, 2);
        return valor - (int) Math.pow(2, bits);
    }
    
    public static void main(String[] args) {
        int[] numeros = {13, 5, 0, -1, -5, -13};
        int bits = 8;
        
        System.out.println("Complemento a dos con " + bits + " bits:");
        System.out.println("Número\tBinario\t\tDecimal");
        System.out.println("------\t------\t\t-------");
        
        for (int numero : numeros) {
            String binario = complementoADos(numero, bits);
            System.out.println(numero + "\t" + binario + "\t" + desdeComplementoADos(binario));
        }
        
        // Mostrar el rango
        System.out.println("\nRango con " + bits + " bits:");
        System.out.println("Mínimo: " + (-(int) Math.pow(2, bits-1)));
        System.out.println("Máximo: " + ((int) Math.pow(2, bits-1) - 1));
        
        // Demostrar desbordamiento
        System.out.println("\nDemostración de desbordamiento:");
        int maximo = (int) Math.pow(2, bits-1) - 1;
        int desbordamiento = maximo + 1;
        
        System.out.println("Máx: " + maximo + " -> " + complementoADos(maximo, bits));
        System.out.println("Máx+1: " + desbordamiento + " -> " + complementoADos(desbordamiento, bits));
        System.out.println("Esperado: " + (-(int) Math.pow(2, bits-1)) + " -> " + complementoADos(-(int) Math.pow(2, bits-1), bits));
    }
}

4. Aritmética computacional

⚠️ Advertencia: La aritmética binaria puede provocar desbordamientos. Siempre verifica los límites de rango de tus tipos de datos.

public class ArithmeticaComputacional {
    
    // Suma binaria
    public static String sumaBinaria(String a, String b) {
        int longitud = Math.max(a.length(), b.length());
        
        // Rellenar con ceros a la izquierda
        a = String.format("%" + longitud + "s", a).replace(' ', '0');
        b = String.format("%" + longitud + "s", b).replace(' ', '0');
        
        StringBuilder resultado = new StringBuilder();
        int acarreo = 0;
        
        // Sumar de derecha a izquierda
        for (int i = longitud - 1; i >= 0; i--) {
            int suma = acarreo + (a.charAt(i) - '0') + (b.charAt(i) - '0');
            resultado.append(suma % 2);
            acarreo = suma / 2;
        }
        
        // Agregar acarreo final
        if (acarreo > 0) {
            resultado.append(acarreo);
        }
        
        return resultado.reverse().toString();
    }
    
    // Resta binaria (método del complemento a dos)
    public static String restaBinaria(String a, String b) {
        // Negar b (complemento a dos)
        String negado = complementoADosNegar(b);
        
        // a + (-b)
        return sumaBinaria(a, negado);
    }
    
    private static String complementoADosNegar(String binario) {
        // Invertir bits
        StringBuilder invertido = new StringBuilder();
        for (char bit : binario.toCharArray()) {
            invertido.append(bit == '0' ? '1' : '0');
        }
        
        // Sumar 1
        return sumaBinaria(invertido.toString(), "1");
    }
    
    // Multiplicación binaria
    public static String multiplicacionBinaria(String a, String b) {
        int aDecimal = Integer.parseInt(a, 2);
        int bDecimal = Integer.parseInt(b, 2);
        int producto = aDecimal * bDecimal;
        
        return Integer.toBinaryString(producto);
    }
    
    // Aritmética de punto fijo
    public static double puntofijosuma(double a, double b, int decimales) {
        int factor = (int) Math.pow(10, decimales);
        int aInt = (int) Math.round(a * factor);
        int bInt = (int) Math.round(b * factor);
        int resultadoInt = aInt + bInt;
        
        return (double) resultadoInt / factor;
    }
    
    // Representación de punto flotante (simplificada)
    public static void puntoflocanteRepresentacion(double numero) {
        if (numero == 0) {
            System.out.println("0 = 0.0 × 2^0");
            return;
        }
        
        boolean negativo = numero < 0;
        numero = Math.abs(numero);
        
        int exponente = 0;
        
        // Normalizar
        while (numero >= 2.0) {
            numero /= 2.0;
            exponente++;
        }
        
        while (numero < 1.0) {
            numero *= 2.0;
            exponente--;
        }
        
        System.out.println((negativo ? "-" : "") + numero + " × 2^" + exponente);
    }
    
    public static void main(String[] args) {
        System.out.println("=== Aritmética Binaria ===");
        
        String a = "1011";  // 11
        String b = "1101";  // 13
        
        System.out.println("a = " + a + " (" + Integer.parseInt(a, 2) + ")");
        System.out.println("b = " + b + " (" + Integer.parseInt(b, 2) + ")");
        
        String suma = sumaBinaria(a, b);
        System.out.println("a + b = " + suma + " (" + Integer.parseInt(suma, 2) + ")");
        
        String diferencia = restaBinaria(b, a);
        System.out.println("b - a = " + diferencia + " (" + Integer.parseInt(diferencia, 2) + ")");
        
        String producto = multiplicacionBinaria(a, b);
        System.out.println("a × b = " + producto + " (" + Integer.parseInt(producto, 2) + ")");
        
        System.out.println("\n=== Aritmética de Punto Fijo ===");
        double x = 12.34;
        double y = 5.67;
        double sumaFP = puntofijosuma(x, y, 2);
        System.out.println(x + " + " + y + " = " + sumaFP + " (2 decimales)");
        
        System.out.println("\n=== Representación de Punto Flotante ===");
        double[] numeros = {12.5, 0.75, -3.125, 256.0};
        for (double numero : numeros) {
            System.out.print(numero + " = ");
            puntoflocanteRepresentacion(numero);
        }
    }
}

5. Operaciones de bits en Python y sistemas numéricos

# Sistemas numéricos y operaciones de bits en Python

def decimal_a_binario(decimal):
    """Convertir decimal a binario"""
    if decimal == 0:
        return "0"
    
    binario = ""
    while decimal > 0:
        binario = str(decimal % 2) + binario
        decimal //= 2
    
    return binario

def binario_a_decimal(binario):
    """Convertir binario a decimal"""
    return int(binario, 2)

def decimal_a_hex(decimal):
    """Convertir decimal a hexadecimal"""
    digitos_hex = "0123456789ABCDEF"
    
    if decimal == 0:
        return "0"
    
    hex_resultado = ""
    while decimal > 0:
        hex_resultado = digitos_hex[decimal % 16] + hex_resultado
        decimal //= 16
    
    return hex_resultado

def hex_a_decimal(hexadecimal):
    """Convertir hexadecimal a decimal"""
    return int(hexadecimal, 16)

def demo_operaciones_bits():
    """Demostración de operaciones de bits"""
    a = 12  # 1100
    b = 10  # 1010
    
    print(f"a = {a} (Binario: {bin(a)})")
    print(f"b = {b} (Binario: {bin(b)})")
    
    # AND bit a bit
    resultado_and = a & b
    print(f"a & b = {resultado_and} (Binario: {bin(resultado_and)})")
    
    # OR bit a bit
    resultado_or = a | b
    print(f"a | b = {resultado_or} (Binario: {bin(resultado_or)})")
    
    # XOR bit a bit
    resultado_xor = a ^ b
    print(f"a ^ b = {resultado_xor} (Binario: {bin(resultado_xor)})")
    
    # NOT bit a bit
    no_a = ~a
    print(f"~a = {no_a} (Binario: {bin(no_a & 0xFFFFFFFF)})")
    
    # Desplazamiento a la izquierda
    desplazamiento_izq = a << 2
    print(f"a << 2 = {desplazamiento_izq} (Binario: {bin(desplazamiento_izq)})")
    
    # Desplazamiento a la derecha
    desplazamiento_der = a >> 1
    print(f"a >> 1 = {desplazamiento_der} (Binario: {bin(desplazamiento_der)})")

def color_de_rgb(rojo, verde, azul):
    """Combinar RGB como valor de 24 bits"""
    return (rojo << 16) | (verde << 8) | azul

def rgb_de_color(color):
    """Extraer componentes RGB de un color de 24 bits"""
    rojo = (color >> 16) & 0xFF
    verde = (color >> 8) & 0xFF
    azul = color & 0xFF
    return rojo, verde, azul

def demo_manipulacion_bits():
    """Demostración de manipulación de bits"""
    numero = 0b10101000  # 168
    
    print(f"Número original: {numero} (Binario: {bin(numero)})")
    
    # Verificar bit
    posicion_bit = 3
    bit_establecido = (numero & (1 << posicion_bit)) != 0
    print(f"Bit {posicion_bit} establecido: {bit_establecido}")
    
    # Establecer bit
    con_bit = numero | (1 << posicion_bit)
    print(f"Establecer bit {posicion_bit}: {con_bit} (Binario: {bin(con_bit)})")
    
    # Limpiar bit
    sin_bit = numero & ~(1 << posicion_bit)
    print(f"Limpiar bit {posicion_bit}: {sin_bit} (Binario: {bin(sin_bit)})")
    
    # Alternar bit
    alternado = numero ^ (1 << posicion_bit)
    print(f"Alternar bit {posicion_bit}: {alternado} (Binario: {bin(alternado)})")

def programa_principal():
    """Programa principal con todas las demostraciones"""
    print("=== Conversión de sistemas numéricos ===")
    
    numero = 175
    print(f"Decimal {numero}:")
    print(f"  Binario: {decimal_a_binario(numero)}")
    print(f"  Hex: {decimal_a_hex(numero)}")
    
    print("\n=== Operaciones de bits ===")
    demo_operaciones_bits()
    
    print("\n=== Manipulación de bits ===")
    demo_manipulacion_bits()
    
    print("\n=== Colores como RGB ===")
    naranja = color_de_rgb(255, 107, 53)
    print(f"Naranja: #{naranja:06X}")
    
    r, g, b = rgb_de_color(naranja)
    print(f"RGB: ({r}, {g}, {b})")
    
    print("\n=== Complemento a dos (8 bits) ===")
    for i in range(-5, 6):
        if i >= 0:
            binario = format(i, '08b')
        else:
            binario = format((256 + i), '08b')
        print(f"{i:3d}: {binario}")

if __name__ == "__main__":
    programa_principal()

Ejemplos de aplicaciones reales

1. Direcciones IP (IPv4)

Las direcciones IPv4 constan de 4 bytes (32 bits), típicamente expresadas en notación decimal:

Ejemplo: 192.168.1.1

192      168      1       1
↓        ↓        ↓       ↓
11000000 10101000 00000001 00000001
C0       A8       01      01 (Hex)

Binario: 11000000101010000000000100000001
Hex:   C0A80101

Ejemplo de máscara de subred: 255.255.255.0

255      255      255     0
↓        ↓        ↓       ↓
11111111 11111111 11111111 00000000
FF       FF       FF      00 (Hex)

2. Direcciones MAC

Las direcciones MAC tienen 6 bytes (48 bits), típicamente expresadas en notación hexadecimal:

Ejemplo: 00:1A:2B:3C:4D:5E

00:1A:2B:3C:4D:5E
↓  ↓  ↓  ↓  ↓  ↓
00000000:00011010:00101011:00111100:01001101:01011110

Fabricante (OUI): 00:1A:2B
ID del dispositivo: 3C:4D:5E

3. Unicode y UTF-8

Los caracteres Unicode se representan como puntos de código, UTF-8 codifica estos en bytes:

Ejemplo: ‘A’ (U+0041)

Unicode: U+0041
Decimal: 65
Binario:   01000001
UTF-8:   01000001 (1 byte)

Ejemplo: ’€’ (U+20AC)

Unicode: U+20AC
Decimal: 8364
Binario:   00100000 10101100
UTF-8:   11100010 10000011 10101100 (3 bytes)
Hex:     E2 82 AC

4. Códigos de color (RGB)

Los colores web se representan como valores hexadecimales de 24 bits:

Ejemplo: #FF6B35 (Naranja)

#FF6B35
↓↓↓↓↓↓
Rojo  Verde  Azul
FF    6B     35
↓     ↓      ↓
255   107    53

Binario: 11111111 01101011 00110101

Ejemplo en Java:

int naranja = 0xFF6B35;
int rojo = (naranja >> 16) & 0xFF;   // 255
int verde = (naranja >> 8) & 0xFF;   // 107
int azul = naranja & 0xFF;            // 53

5. Tamaños de archivo (binario vs. decimal)

Prefijos binarios (IEC 80000-13):

1 KiB = 2¹⁰ = 1,024 bytes
1 MiB = 2²⁰ = 1,048,576 bytes
1 GiB = 2³⁰ = 1,073,741,824 bytes

Prefijos decimales (SI):

1 KB = 10³ = 1,000 bytes
1 MB = 10⁶ = 1,000,000 bytes
1 GB = 10⁹ = 1,000,000,000 bytes

Ejemplo: 1 GB de disco duro

Fabricante (decimal): 1,000,000,000 bytes
Sistema operativo (binario): 1,000,000,000 ÷ 1,073,741,824 = 0.93 GB

6. Tabla ASCII (fragmento)

CarácterDecimalHexBinario
A654101000001
B664201000010
C674301000011
a976101100001
b986201100010
c996301100011
0483000110000
9573900111001

Ejercicios interactivos

Tareas de conversión

Ejercicio 1: Decimal a binario Convierte los siguientes números decimales a binario:

  • 42
  • 127
  • 255
Ver soluciones
42₁₀ = 101010₂
127₁₀ = 1111111₂
255₁₀ = 11111111₂

Ejercicio 2: Binario a decimal Convierte los siguientes números binarios a decimal:

  • 10101
  • 11110000
  • 10000000
Ver soluciones
10101₂ = 21₁₀
11110000₂ = 240₁₀
10000000₂ = 128₁₀

Ejercicio 3: Decimal a hexadecimal Convierte los siguientes números decimales a hexadecimal:

  • 100
  • 255
  • 4095
Ver soluciones
100₁₀ = 64₁₆
255₁₀ = FF₁₆
4095₁₀ = FFF₁₆

Ejercicio 4: Hexadecimal a binario Convierte los siguientes números hexadecimales a binario:

  • A5
  • FF
  • 1B
Ver soluciones
A5₁₆ = 10100101₂
FF₁₆ = 11111111₂
1B₁₆ = 00011011₂

Cuestionario de operaciones de bits

Pregunta 1: ¿Cuál es el resultado de 12 & 10?

Ver solución
12₁₀ = 1100₂
10₁₀ = 1010₂
1100 & 1010 = 1000₂ = 8₁₀

Pregunta 2: ¿Cuál es el resultado de 12 | 10?

Ver solución
12₁₀ = 1100₂
10₁₀ = 1010₂
1100 | 1010 = 1110₂ = 14₁₀

Pregunta 3: ¿Cuál es el resultado de 12 ^ 10?

Ver solución
12₁₀ = 1100₂
10₁₀ = 1010₂
1100 ^ 1010 = 0110₂ = 6₁₀

Pregunta 4: ¿Cuál es el resultado de 8 << 2?

Ver solución
8₁₀ = 1000₂
1000 << 2 = 100000₂ = 32₁₀

Pregunta 5: ¿Cuál es el resultado de 16 >> 2?

Ver solución
16₁₀ = 10000₂
10000 >> 2 = 100₂ = 4₁₀

Tareas de Desafío

Tarea 6: Manipulación de Bits Dada la número 42 (101010₂). Establece el 3.er bit (desde la derecha, indexado desde 0).

Mostrar solución
42 = 101010₂
3.er bit desde la derecha = posición de bit 2
42 | (1 << 2) = 42 | 4 = 46₁₀ = 101110₂

Tarea 7: Complemento a Dos Representa -5 en complemento a dos de 8 bits.

Mostrar solución
5₁₀ = 00000101₂
Invertir: 11111010₂
+ 1:      11111011₂

-5₁₀ = 11111011₂ (complemento a dos de 8 bits)

Tarea 8: Dirección IP Convierte la dirección IP 10.0.0.1 a hexadecimal.

Mostrar solución
10.0.0.1
↓ ↓ ↓ ↓
0A 00 00 01

Hex: 0A000001

Errores Comunes y Trampas

1. Errores de Límite en Posiciones de Bits

Problema: Las posiciones de bits se cuentan frecuentemente de forma incorrecta (indexado desde 0 vs. indexado desde 1)

// ❌ Incorrecto: La posición de bit 3 es el 4.o bit desde la derecha
int bitPosition = 3;
boolean bitSet = (zahl & (1 << bitPosition)) != 0; // Verifica bit 3 (4.o bit)

// ✅ Correcto: Documentación clara de la indexación
// Posición de bit 0 = bit más a la derecha (LSB)
// Posición de bit 7 = bit más a la izquierda en 8 bits
int bitPosition = 2; // 3.er bit desde la derecha (indexado desde 0)
boolean bitSet = (zahl & (1 << bitPosition)) != 0;

2. Tratamiento de Signos en Desplazamientos

Problema: El desplazamiento a la derecha se comporta diferente para valores con y sin signo

// Java: Desplazamiento a la derecha con signo (>>) rellena con el bit de signo
int x = -8; // 11111111111111111111111111111000
int result = x >> 2; // 11111111111111111111111111111110 (-2)

// Desplazamiento a la derecha sin signo (>>>) rellena con ceros
int x = -8;
int result = x >>> 2; // 00111111111111111111111111111110 (1073741822)

3. Problemas de Desbordamiento

Problema: El desbordamiento de enteros se ignora frecuentemente

// ❌ Sin verificación de desbordamiento
int max = Integer.MAX_VALUE; // 2147483647
int overflow = max + 1;      // -2147483648 (¡desbordamiento!)

// ✅ Con verificación de desbordamiento
if (max == Integer.MAX_VALUE) {
    throw new ArithmeticException("Integer overflow");
}

4. Malos Entendidos sobre Convenciones Hexadecimales

Problema: Diferentes formas de escribir números hexadecimales

// Diferentes convenciones:
0xFF      // Prefijo Java/C
0xFF      // Prefijo C#
0xFF      // Prefijo JavaScript
#FF       // CSS/Web
FFh       // Ensamblador
$FF       // Pascal

5. Olvidar Endianness

Problema: Se ignora el orden de bytes

// Little Endian (x86, ARM): byte menos significativo primero
int valor = 0x12345678;
// En memoria: 78 56 34 12

// Big Endian (Network, PowerPC): byte más significativo primero
// En memoria: 12 34 56 78

// Conversión:
int littleEndian = 0x12345678;
int bigEndian = Integer.reverseBytes(littleEndian); // 0x78563412

6. Base Incorrecta en parseInt

Problema: La base no se especifica correctamente

// ❌ Incorrecto: Decimal en lugar de binario
int valor = Integer.parseInt("1010"); // 1010 (decimal)

// ✅ Correcto: Especificar la base
int valor = Integer.parseInt("1010", 2); // 10 (decimal)
int valorHex = Integer.parseInt("FF", 16); // 255 (decimal)

7. Exceder el Rango de Complemento a Dos

Problema: El número no cabe en el ancho de bit especificado

// Complemento a dos de 8 bits: -128 a 127
int valor = 200; // ❌ ¡Excede el rango!

// Verificación de rango:
if (valor < -128 || valor > 127) {
    throw new IllegalArgumentException("Valor fuera del rango de 8 bits");
}

8. División de Float vs. Entero

Problema: La división de enteros trunca los decimales

// ❌ Incorrecto: División de enteros
int resultado = 5 / 2; // 2 (en lugar de 2.5)

// ✅ Correcto: División de floats
double resultado = 5.0 / 2.0; // 2.5

9. Negación vs. Complemento

Problema: Confusión entre negación lógica y complemento de bits

// NOT lógico (!)
boolean a = true;
boolean b = !a; // false

// NOT de bits (~)
int x = 5;      // 0101
int y = ~x;     // 1010 (con complemento a dos: -6)

10. Ceros Iniciales en Hexadecimal

Problema: Los ceros iniciales cambian el significado en ciertos contextos

// En Java: 0x0F = 15, 0xF = 15 (iguales)
// Pero en comparaciones de strings:
String a = "0F";
String b = "F";
// a.equals(b) = false!

Consejos Prácticos para Desarrolladores

1. Depuración con Vista Binaria/Hexadecimal

Usar herramientas del IDE:

// IntelliJ/Eclipse: Vista binaria en el depurador
// Variable: 175
// Binaria: 10101111
// Hex:     AF

Logging con diferentes bases:

System.out.println("Decimal: " + zahl);
System.out.println("Binaria: " + Integer.toBinaryString(zahl));
System.out.println("Hex: " + Integer.toHexString(zahl));
System.out.println("Octal: " + Integer.toOctalString(zahl));

2. Flags de Bits en APIs

Flags de estado eficientes:

// Definir constantes de flags
public static final int READ = 1 << 0;   // 0001
public static final int WRITE = 1 << 1;  // 0010
public static final int EXECUTE = 1 << 2; // 0100

// Combinar flags
int permissions = READ | WRITE; // 0011

// Verificar flags
boolean canRead = (permissions & READ) != 0;
boolean canWrite = (permissions & WRITE) != 0;
boolean canExecute = (permissions & EXECUTE) != 0;

3. Ventajas de Rendimiento de Operaciones de Bits

Multiplicación/División mediante desplazamientos:

// Más rápido que * 2
int x = 5;
int duplicado = x << 1; // 10

// Más rápido que / 2
int mitad = x >> 1; // 2

// Módulo 2^n con AND
int modulo8 = x & 0x7; // x % 8

⚠️ Precaución: Los compiladores modernos a menudo optimizan automáticamente, úsalo solo en escenarios de rendimiento críticos.

4. Manipular Valores de Color

Procesar colores RGB eficientemente:

// Crear color
int color = (255 << 16) | (107 << 8) | 53; // #FF6B35

// Extraer componentes
int rojo = (color >> 16) & 0xFF;
int verde = (color >> 8) & 0xFF;
int azul = color & 0xFF;

// Manipular color
int nuevoColor = color & 0x00FFFFFF; // Borrar componente roja
nuevoColor |= (200 << 16); // Establecer nuevo valor rojo

5. Byte Order de Red

Garantizar portabilidad:

// Host to Network Short (16 bits)
short puertoHost = 8080;
short puertoRed = Short.reverseBytes(puertoHost);

// Network to Host Long (32 bits)
int direccionRed = 0xC0A80101; // 192.168.1.1
int direccionHost = Integer.reverseBytes(direccionRed);

6. Máscaras de Bits para Configuración

Almacenar configuración de forma compacta:

public class Config {
    private int flags = 0;
    
    public static final int DEBUG = 1 << 0;
    public static final int VERBOSE = 1 << 1;
    public static final int LOGGING = 1 << 2;
    
    public void setFlag(int flag) {
        flags |= flag;
    }
    
    public void clearFlag(int flag) {
        flags &= ~flag;
    }
    
    public boolean hasFlag(int flag) {
        return (flags & flag) != 0;
    }
}

7. Estructuras de Datos Eficientes en Memoria

Bits para valores booleanos:

// En lugar de 8 booleans (8 bytes):
// boolean[] flags = new boolean[8];

// Un int (4 bytes) para 8 flags:
int flags = 0;
flags |= (1 << 3); // Establecer 3.er flag

8. Funciones Hash con Operaciones de Bits

Función hash simple:

public static int simpleHash(String s) {
    int hash = 0;
    for (int i = 0; i < s.length(); i++) {
        hash = (hash << 5) - hash + s.charAt(i);
    }
    return hash;
}

9. Cálculo de paridad

Bit de paridad para detección de errores:

public static boolean calculateParity(int x) {
    boolean parity = false;
    while (x != 0) {
        parity = !parity;
        x = x & (x - 1); // Elimina el bit establecido de menor valor
    }
    return parity;
}

10. Verificación de potencia de 2

Verificación eficiente de si un número es potencia de dos:

public static boolean isPowerOfTwo(int x) {
    return x > 0 && (x & (x - 1)) == 0;
}

// Ejemplos:
// isPowerOfTwo(1) = true  (2⁰)
// isPowerOfTwo(2) = true  (2¹)
// isPowerOfTwo(4) = true  (2²)
// isPowerOfTwo(8) = true  (2³)
// isPowerOfTwo(6) = false

Temas avanzados

1. BCD (Decimal Codificado en Binario)

BCD codifica cada dígito decimal en 4 bits:

Ejemplo: 42 en BCD

4      2
↓      ↓
0100   0010

BCD: 01000010

Ventajas:

  • Conversión simple hacia y desde decimal
  • Aritmética decimal precisa

Desventajas:

  • Uso ineficiente de memoria (6 de 16 valores sin usar)
  • Aritmética más compleja

Implementación en Java:

public class BCD {
    public static int decimalToBCD(int decimal) {
        int bcd = 0;
        int shift = 0;
        
        while (decimal > 0) {
            int digit = decimal % 10;
            bcd |= (digit << shift);
            decimal /= 10;
            shift += 4;
        }
        
        return bcd;
    }
    
    public static int bcdToDecimal(int bcd) {
        int decimal = 0;
        int multiplier = 1;
        
        while (bcd > 0) {
            int digit = bcd & 0xF;
            decimal += digit * multiplier;
            bcd >>= 4;
            multiplier *= 10;
        }
        
        return decimal;
    }
}

2. Código Gray

El código Gray es un sistema numérico binario donde valores consecutivos difieren en un solo bit:

Tabla de código Gray (4 bits):

Decimal | Binario | Código Gray
--------|---------|-------------
0       | 0000    | 0000
1       | 0001    | 0001
2       | 0010    | 0011
3       | 0011    | 0010
4       | 0100    | 0110
5       | 0101    | 0111
6       | 0110    | 0101
7       | 0111    | 0100

Binario a código Gray:

public static int binaryToGray(int binary) {
    return binary ^ (binary >> 1);
}

Código Gray a binario:

public static int grayToBinary(int gray) {
    int binary = gray;
    while (gray > 0) {
        gray >>= 1;
        binary ^= gray;
    }
    return binary;
}

Aplicación: Codificadores rotativos, minimización de errores en conmutadores

3. CRC (Comprobación de Redundancia Cíclica)

CRC es un método de detección de errores para transmisión de datos:

Implementación simple de CRC-8:

public class CRC8 {
    private static final int POLYNOMIAL = 0x07;
    
    public static int calculate(byte[] data) {
        int crc = 0x00;
        
        for (byte b : data) {
            crc ^= b;
            
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x80) != 0) {
                    crc = (crc << 1) ^ POLYNOMIAL;
                } else {
                    crc <<= 1;
                }
            }
        }
        
        return crc & 0xFF;
    }
}

Usos:

  • Protocolos de red (Ethernet, USB)
  • Verificación de integridad de archivos
  • Detección de errores en memoria

4. Formato de punto flotante IEEE 754

Estándar para números en punto flotante:

Precisión simple de 32 bits:

Bit 31:     Signo (S)
Bits 30-23: Exponente (E, 8 bits)
Bits 22-0:  Mantisa (M, 23 bits)

Valor = (-1)^S × 2^(E-127) × (1 + M)

Ejemplo: 12.5 en IEEE 754

12.5 = 1100.1₂ = 1.1001₂ × 2³

S = 0 (positivo)
E = 127 + 3 = 130 = 10000010₂
M = 10010000000000000000000

IEEE 754: 01000001010010000000000000000000
Hex:      41480000

Implementación en Java:

public class IEEE754 {
    public static String floatToBits(float value) {
        int bits = Float.floatToIntBits(value);
        return String.format("%32s", Integer.toBinaryString(bits))
                       .replace(' ', '0');
    }
    
    public static float bitsToFloat(String bits) {
        int intValue = Integer.parseInt(bits, 2);
        return Float.intBitsToFloat(intValue);
    }
}

5. Endianness (Orden de bytes)

Big Endian: Byte más significativo primero (Network Byte Order) Little Endian: Byte menos significativo primero (x86, ARM)

Ejemplo: 0x12345678

Big Endian:    12 34 56 78
Little Endian: 78 56 34 12

Conversión en Java:

public class Endianness {
    // Little Endian a Big Endian
    public static int littleToBig(int value) {
        return Integer.reverseBytes(value);
    }
    
    // Big Endian a Little Endian
    public static int bigToLittle(int value) {
        return Integer.reverseBytes(value);
    }
    
    // Verificar la endianness del sistema
    public static boolean isLittleEndian() {
        int test = 0x12345678;
        byte[] bytes = new byte[] {
            (byte) (test >> 24),
            (byte) (test >> 16),
            (byte) (test >> 8),
            (byte) test
        };
        return bytes[0] == 0x78; // Little Endian
    }
}

Importante en:

  • Protocolos de red (TCP/IP utiliza Big Endian)
  • Formatos de archivo
  • Desarrollo multiplataforma

6. Bit de paridad

Método simple de detección de errores:

Paridad par:

Datos: 1011
Paridad: 0 (para que la cantidad de 1s sea par: 10110 → 3 unos = impar ❌)
Paridad: 1 (para que la cantidad de 1s sea par: 10111 → 4 unos = par ✅)

Implementación en Java:

public class Parity {
    public static boolean calculateEvenParity(byte data) {
        int count = Integer.bitCount(data & 0xFF);
        return (count % 2) == 0;
    }
    
    public static byte setEvenParity(byte data) {
        if (calculateEvenParity(data)) {
            return (byte) (data & 0x7F); // Bit de paridad = 0
        } else {
            return (byte) (data | 0x80); // Bit de paridad = 1
        }
    }
}

7. Sumas de verificación

Sumas de verificación simples para integridad de datos:

Suma de verificación simple:

public class Checksum {
    public static int calculate(byte[] data) {
        int sum = 0;
        for (byte b : data) {
            sum += (b & 0xFF);
        }
        return sum & 0xFFFF; // Suma de verificación de 16 bits
    }
    
    public static boolean verify(byte[] data, int checksum) {
        return calculate(data) == checksum;
    }
}

8. Codificación Base64

Codificar datos binarios en caracteres ASCII:

Ejemplo:

Binario: 01000001 01000010 01000011 (ABC)
Base64: QUJD

Implementación en Java:

import java.util.Base64;

public class Base64Example {
    public static String encode(String input) {
        return Base64.getEncoder().encodeToString(input.getBytes());
    }
    
    public static String decode(String encoded) {
        byte[] decoded = Base64.getDecoder().decode(encoded);
        return new String(decoded);
    }
}

9. Rotación de bits

Rotación sin pérdida de bits:

Rotación a la izquierda:

public static int rotateLeft(int value, int shift) {
    return (value << shift) | (value >>> (32 - shift));
}

Rotación a la derecha:

public static int rotateRight(int value, int shift) {
    return (value >>> shift) | (value << (32 - shift));
}

Ejemplo:

int x = 0b11000000; // 192
int rotated = rotateLeft(x, 2); // 0b00000011 (3)

10. Distancia de Hamming

Número de bits diferentes entre dos valores:

public static int hammingDistance(int a, int b) {
    int xor = a ^ b;
    return Integer.bitCount(xor);
}

// Ejemplo:
// hammingDistance(0b1010, 0b1100) = 2
// 1010 ^ 1100 = 0110 (2 unos)

Aplicación: Códigos de corrección de errores, criptografía

Tabla de conversión

DecimalBinarioHexadecimalOctal
0000000
1000111
2001022
3001133
4010044
5010155
6011066
7011177
81000810
91001911
101010A12
111011B13
121100C14
131101D15
141110E16
151111F17

Resumen de operaciones con bits

Bit-Operationen

| Operador | Símbolo | Descripción | Ejemplo |
|----------|---------|-------------|----------|
| AND | & | AND bit a bit | 5 & 3 = 1 |
| OR | \| | OR bit a bit | `5 \| 3 = 7` |
| XOR | ^ | OR exclusivo | `5 ^ 3 = 6` |
| NOT | ~ | NOT bit a bit | `~5 = -6` |
| Left Shift | << | Desplazamiento a la izquierda | `5 << 2 = 20` |
| Right Shift | >> | Desplazamiento a la derecha | `5 >> 1 = 2` |
| Unsigned Right Shift | >>> | Desplazamiento a la derecha sin signo | `5 >>> 1 = 2` |

Tamaños de memoria

UnidadBytesBitsRango (sin signo)
Byte180 - 255
Word2160 - 65,535
DWord4320 - 4,294,967,295
QWord8640 - 18,446,744,073,709,551,615

Rango del complemento a dos

BitsMínimoMáximo
8-128127
16-32,76832,767
32-2,147,483,6482,147,483,647
64-9,223,372,036,854,775,8089,223,372,036,854,775,807

Ventajas e inconvenientes

Ventajas del sistema binario

  • Simple: Solo dos estados (0 y 1)
  • Confiable: Fácil de implementar sin errores
  • Eficiente: Óptimo para circuitos electrónicos
  • Universal: Base de todos los sistemas digitales

Ventajas del sistema hexadecimal

  • Compacto: 4 dígitos binarios = 1 dígito hexadecimal
  • Legible: Más corto y claro que el binario
  • Estándar: Ampliamente utilizado en programación
  • Práctico: Ideal para direcciones y colores

Inconvenientes

  • Abstracción: No intuitivo para las personas
  • Conversión: Requiere aritmética mental
  • Propenso a errores: Fácil equivocarse en conversiones manuales

Preguntas frecuentes de examen

1. **Convierta 175 (decimal) a binario y hexadecimal.**
   175₁₀ = 10101111= AF₁₆

2. **¿Cuál es el resultado de 12 & 10 (AND bit a bit)?**
   12₁₀ = 1100₂, 10₁₀ = 1010₂ → 1100 & 1010 = 1000= 8₁₀

3. **Explique el complemento a dos.**
   Método para representar números negativos en binario mediante inversión de bits y suma de 1.

4. **¿Para qué se utilizan los números hexadecimales?**
   Representación compacta de valores binarios, colores, direcciones de memoria, códigos de error.

Fuentes principales

  1. https://de.wikipedia.org/wiki/Zahlensystem
  2. https://de.wikipedia.org/wiki/Zweierkomplement
  3. https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
Volver al blog
Share:

Entradas relacionadas