Skip to content
IRC-CodingIRC-Coding
Системы счисленияДвоичная шестнадцатеричнаяBit операцииПреобразованиеКомпьютерная арифметика

Системы счисления: двоичная, шестнадцатеричная, десятичная

Системы счисления и преобразование. Двоичная, шестнадцатеричная, десятичная с примерами, bit операции, дополнение и компьютерная арифметика.

S

schutzgeist

27 min read
Системы счисления: двоичная, шестнадцатеричная, десятичная

Системы счисления: двоичная, шестнадцатеричная, десятичная и битовые операции

Полное руководство по системам счисления, включая двоичную, шестнадцатеричную, десятичную, преобразование между ними и битовые операции с практическими примерами.

Суть вопроса

Компьютеры используют двоичную систему (0 и 1). Шестнадцатеричная система служит компактным представлением двоичных значений. Битовые операции позволяют напрямую манипулировать данными на уровне битов.

Путь обучения

Начинающие (шаги 1-3):

  1. Разберитесь с основами систем счисления (десятичная, двоичная, шестнадцатеричная)
  2. Потренируйтесь в простых преобразованиях (десятичная ↔ двоичная ↔ шестнадцатеричная)
  3. Изучите базовые битовые операции (AND, OR, XOR)

Продвинутый уровень (шаги 4-6): 4. Поймите дополнительный код и представление отрицательных чисел 5. Практические применения (IP-адреса, цвета, MAC-адреса) 6. Операции сдвига и манипуляция битами

Эксперты (шаги 7-10): 7. Продвинутые темы (BCD, код Грея, CRC) 8. Формат чисел с плавающей запятой IEEE 754 9. Порядок байтов и сетевой порядок байтов 10. Оптимизация производительности с помощью битовых операций

💡 Совет: Начните с интерактивных упражнений, чтобы проверить своё понимание!

Определение ключевых понятий

Системы счисления — это методы представления чисел с использованием различных оснований. Компьютеры изнутри используют двоичную систему (основание 2), тогда как люди предпочитают десятичную систему (основание 10).

Основные системы счисления:

Десятичная система (основание 10)

  • Цифры: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
  • Позиционные значения: 10⁰, 10¹, 10², 10³, …
  • Применение: Общение между людьми, повседневная математика
  • Пример: 123 = 1×10² + 2×10¹ + 3×10⁰

Визуальное представление:

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

Двоичная система (основание 2)

  • Цифры: 0, 1
  • Позиционные значения: 2⁰, 2¹, 2², 2³, …
  • Применение: Внутреннее представление в компьютере
  • Пример: 1011 = 1×2³ + 0×2² + 1×2¹ + 1×2⁰ = 11

⚠️ Важно: Компьютеры работают только с 0 и 1. Все данные в конечном итоге хранятся как двоичные числа!

Визуальное представление:

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

Шестнадцатеричная система (основание 16)

  • Цифры: 0-9, A, B, C, D, E, F
  • Позиционные значения: 16⁰, 16¹, 16², 16³, …
  • Применение: Компактное представление двоичных значений, цвета, адреса памяти
  • Пример: A3 = 10×16¹ + 3×16⁰ = 163

💡 Совет: 1 шестнадцатеричная цифра = 4 двоичные цифры. Шестнадцатеричная система идеальна для представления двоичных данных, так как она компактнее и понятнее!

Визуальное представление:

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

Восьмеричная система (основание 8)

  • Цифры: 0, 1, 2, 3, 4, 5, 6, 7
  • Позиционные значения: 8⁰, 8¹, 8², 8³, …
  • Применение: Исторически используется в Unix-системах
  • Пример: 75 = 7×8¹ + 5×8⁰ = 61

📜 История: Восьмеричная система часто применялась в ранних Unix-системах (например, права доступа в chmod 755). Сегодня шестнадцатеричная система распространена больше.

Визуальное представление:

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

Ключевые пункты для подготовки

  • Десятичная система: основание 10, цифры 0-9, позиционные значения 10ⁿ
  • Двоичная система: основание 2, цифры 0-1, представление в компьютере
  • Шестнадцатеричная система: основание 16, цифры 0-9, A-F, компактное представление
  • Преобразование: деление/умножение на основание, метод позиционных значений
  • Битовые операции: AND, OR, XOR, NOT, операции сдвига
  • Дополнительный код: отрицательные числа в двоичной системе
  • Релевантно для экзаменов: основа компьютерной архитектуры и программирования

Основные компоненты

  1. Системы счисления: десятичная, двоичная, шестнадцатеричная, восьмеричная
  2. Преобразование: между различными основаниями
  3. Битовые операции: AND, OR, XOR, NOT, сдвиг
  4. Дополнительный код: отрицательные числа
  5. Компьютерная арифметика: сложение, вычитание, умножение
  6. Типы данных: биты, байты, слова
  7. Представление памяти: шестнадцатеричные адреса
  8. Обнаружение ошибок: чётность, контрольные суммы

Пошаговые примеры преобразований

💡 Совет: Регулярно отрабатывайте эти преобразования, они являются основой для понимания компьютерных систем! Кроме того, они встречаются почти во всех экзаменационных заданиях.

Наш бесплатный онлайн и офлайн калькулятор:

Zahlensystem-Umrechnung

Десятичная в двоичную (деление на 2)

Пример: 13₁₀ → двоичная

13 ÷ 2 = 6 остаток 1  ← младший бит
 6 ÷ 2 = 3 остаток 0
 3 ÷ 2 = 1 остаток 1
 1 ÷ 2 = 0 остаток 1  ← старший бит

Читаем снизу вверх: 1101₂

Двоичная в десятичную (метод позиционных значений)

Пример: 1101₂ → десятичная

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₁₀

Десятичная в шестнадцатеричную (деление на 16)

Пример: 255₁₀ → шестнадцатеричная

255 ÷ 16 = 15 остаток 15 (F)
 15 ÷ 16 =  0 остаток 15 (F)

Читаем снизу вверх: FF₁₆

Шестнадцатеричная в двоичную (4-битные группы)

Пример: A3₁₆ → двоичная

A (10)      3
↓           ↓
1010        0011

Итого: 10100011₂

Двоичная в шестнадцатеричную (4-битные группы)

Пример: 10100011₂ → шестнадцатеричная

1010 0011
↓     ↓
A     3

Итого: A3₁₆

Примеры из практики

1. Преобразование между системами счисления

public class ZahlensystemUmrechnung {
    
    // Dezimal zu Binär
    public static String dezimalZuBinaer(int dezimal) {
        if (dezimal == 0) return "0";
        
        StringBuilder binaer = new StringBuilder();
        while (dezimal > 0) {
            binaer.append(dezimal % 2);
            dezimal /= 2;
        }
        return binaer.reverse().toString();
    }
    
    // Binär zu Dezimal
    public static int binaerZuDezimal(String binaer) {
        int dezimal = 0;
        int potenz = 0;
        
        for (int i = binaer.length() - 1; i >= 0; i--) {
            if (binaer.charAt(i) == '1') {
                dezimal += Math.pow(2, potenz);
            }
            potenz++;
        }
        return dezimal;
    }
    
    // Dezimal zu Hexadezimal
    public static String dezimalZuHex(int dezimal) {
        if (dezimal == 0) return "0";
        
        char[] hexZiffern = {'0', '1', '2', '3', '4', '5', '6', '7', 
                           '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        StringBuilder hex = new StringBuilder();
        
        while (dezimal > 0) {
            int rest = dezimal % 16;
            hex.append(hexZiffern[rest]);
            dezimal /= 16;
        }
        return hex.reverse().toString();
    }
    
    // Hexadezimal zu Dezimal
    public static int hexZuDezimal(String hex) {
        int dezimal = 0;
        String hexZiffern = "0123456789ABCDEF";
        
        for (int i = 0; i < hex.length(); i++) {
            char ziffer = hex.charAt(i);
            int wert = hexZiffern.indexOf(ziffer);
            dezimal = dezimal * 16 + wert;
        }
        return dezimal;
    }
    
    // Binär zu Hexadezimal
    public static String binaerZuHex(String binaer) {
        // Binär in 4er-Gruppen aufteilen
        while (binaer.length() % 4 != 0) {
            binaer = "0" + binaer;
        }
        
        StringBuilder hex = new StringBuilder();
        for (int i = 0; i < binaer.length(); i += 4) {
            String nibble = binaer.substring(i, i + 4);
            int wert = binaerZuDezimal(nibble);
            hex.append(dezimalZuHex(wert));
        }
        return hex.toString();
    }
    
    // Hexadezimal zu Binär
    public static String hexZuBinaer(String hex) {
        String[] binaerMap = {
            "0000", "0001", "0010", "0011",
            "0100", "0101", "0110", "0111",
            "1000", "1001", "1010", "1011",
            "1100", "1101", "1110", "1111"
        };
        
        StringBuilder binaer = new StringBuilder();
        for (int i = 0; i < hex.length(); i++) {
            char ziffer = hex.charAt(i);
            int wert = "0123456789ABCDEF".indexOf(ziffer);
            binaer.append(binaerMap[wert]);
        }
        
        // Führende Nullen entfernen
        while (binaer.length() > 1 && binaer.charAt(0) == '0') {
            binaer.deleteCharAt(0);
        }
        
        return binaer.toString();
    }
    
    public static void main(String[] args) {
        int zahl = 175;
        
        System.out.println("Zahl: " + zahl);
        System.out.println("Binär: " + dezimalZuBinaer(zahl));
        System.out.println("Hex: " + dezimalZuHex(zahl));
        
        String binaer = "10101111";
        String hex = "AF";
        
        System.out.println("\nBinär " + binaer + " = Dezimal " + binaerZuDezimal(binaer));
        System.out.println("Hex " + hex + " = Dezimal " + hexZuDezimal(hex));
        System.out.println("Binär " + binaer + " = Hex " + binaerZuHex(binaer));
        System.out.println("Hex " + hex + " = Binär " + hexZuBinaer(hex));
    }
}

2. Побитовые операции в разных языках

// Java побитовые операции
public class BitOperationen {
    
    public static void main(String[] args) {
        int a = 12;  // 1100 в двоичной
        int b = 10;  // 1010 в двоичной
        
        System.out.println("a = " + a + " (двоичная: " + Integer.toBinaryString(a) + ")");
        System.out.println("b = " + b + " (двоичная: " + Integer.toBinaryString(b) + ")");
        
        // Побитовое И (&)
        int and = a & b;  // 1100 & 1010 = 1000 (8)
        System.out.println("a & b = " + and + " (двоичная: " + Integer.toBinaryString(and) + ")");
        
        // Побитовое ИЛИ (|)
        int or = a | b;   // 1100 | 1010 = 1110 (14)
        System.out.println("a | b = " + or + " (двоичная: " + Integer.toBinaryString(or) + ")");
        
        // Побитовое исключающее ИЛИ (^)
        int xor = a ^ b;  // 1100 ^ 1010 = 0110 (6)
        System.out.println("a ^ b = " + xor + " (двоичная: " + Integer.toBinaryString(xor) + ")");
        
        // Побитовое НЕ (~)
        int notA = ~a;   // ~1100 = 0011 (с дополнением до двух)
        System.out.println("~a = " + notA + " (двоичная: " + Integer.toBinaryString(notA) + ")");
        
        // Левый сдвиг (<<)
        int leftShift = a << 2;  // 1100 << 2 = 110000 (48)
        System.out.println("a << 2 = " + leftShift + " (двоичная: " + Integer.toBinaryString(leftShift) + ")");
        
        // Правый сдвиг (>>)
        int rightShift = a >> 1;  // 1100 >> 1 = 0110 (6)
        System.out.println("a >> 1 = " + rightShift + " (двоичная: " + Integer.toBinaryString(rightShift) + ")");
        
        // Беззнаковый правый сдвиг (>>>)
        int unsignedRightShift = a >>> 1;  // 1100 >>> 1 = 0110 (6)
        System.out.println("a >>> 1 = " + unsignedRightShift + " (двоичная: " + Integer.toBinaryString(unsignedRightShift) + ")");
        
        // Практическое применение
        praktischeAnwendungen();
    }
    
    private static void praktischeAnwendungen() {
        System.out.println("\n=== Практическое применение ===");
        
        // Проверить бит (установлен ли 3-й бит?)
        int zahl = 12;  // 1100
        int bitPosition = 2;
        boolean bitSet = (zahl & (1 << bitPosition)) != 0;
        System.out.println("Бит " + bitPosition + " в " + zahl + " установлен: " + bitSet);
        
        // Установить бит
        int mitBit = zahl | (1 << bitPosition);
        System.out.println("Установить бит " + bitPosition + ": " + mitBit);
        
        // Очистить бит
        int ohneBit = zahl & ~(1 << bitPosition);
        System.out.println("Очистить бит " + bitPosition + ": " + ohneBit);
        
        // Переключить бит
        int umgeschaltet = zahl ^ (1 << bitPosition);
        System.out.println("Переключить бит " + bitPosition + ": " + umgeschaltet);
        
        // Извлечь цвет из RGB-значений
        int farbe = 0xFF6B35;  // Оранжевый
        int rot = (farbe >> 16) & 0xFF;
        int gruen = (farbe >> 8) & 0xFF;
        int blau = farbe & 0xFF;
        
        System.out.println("\nЦвет: #" + Integer.toHexString(farbe).toUpperCase());
        System.out.println("Красный: " + rot);
        System.out.println("Зелёный: " + gruen);
        System.out.println("Синий: " + blau);
    }
}

3. Дополнение до двух для отрицательных чисел

⚠️ Важно: дополнение до двух это стандарт представления отрицательных чисел в современных компьютерах. Избегайте дополнения до единицы и прямого кода с знаком.

public class Zweierkomplement {
    
    // Вычислить дополнение до двух
    public static String zweierkomplement(int zahl, int bits) {
        if (zahl >= 0) {
            return String.format("%" + bits + "s", Integer.toBinaryString(zahl)).replace(' ', '0');
        }
        
        // Отрицательное число: 2^bits - |zahl|
        int positiv = (int) (Math.pow(2, bits) + zahl);
        return String.format("%" + bits + "s", Integer.toBinaryString(positiv)).replace(' ', '0');
    }
    
    // Преобразовать из дополнения до двух в десятичное число
    public static int vonZweierkomplement(String binaer) {
        int bits = binaer.length();
        
        // Если старший бит равен 0, число положительное
        if (binaer.charAt(0) == '0') {
            return Integer.parseInt(binaer, 2);
        }
        
        // Отрицательное число: - (2^bits - значение)
        int wert = Integer.parseInt(binaer, 2);
        return wert - (int) Math.pow(2, bits);
    }
    
    public static void main(String[] args) {
        int[] zahlen = {13, 5, 0, -1, -5, -13};
        int bits = 8;
        
        System.out.println("Дополнение до двух с " + bits + " битами:");
        System.out.println("Число\tДвоичная\tДесятичная");
        System.out.println("-----\t--------\t----------");
        
        for (int zahl : zahlen) {
            String binaer = zweierkomplement(zahl, bits);
            System.out.println(zahl + "\t" + binaer + "\t" + vonZweierkomplement(binaer));
        }
        
        // Показать диапазон
        System.out.println("\nДиапазон при " + bits + " битах:");
        System.out.println("Минимум: " + (-(int) Math.pow(2, bits-1)));
        System.out.println("Максимум: " + ((int) Math.pow(2, bits-1) - 1));
        
        // Демонстрация переполнения
        System.out.println("\nДемонстрация переполнения:");
        int max = (int) Math.pow(2, bits-1) - 1;
        int ueberlauf = max + 1;
        
        System.out.println("Макс: " + max + " -> " + zweierkomplement(max, bits));
        System.out.println("Макс+1: " + ueberlauf + " -> " + zweierkomplement(ueberlauf, bits));
        System.out.println("Ожидается: " + (-(int) Math.pow(2, bits-1)) + " -> " + zweierkomplement(-(int) Math.pow(2, bits-1), bits));
    }
}

4. Компьютерная арифметика

⚠️ Внимание: двоичная арифметика может привести к переполнению. Всегда проверяйте границы диапазона ваших типов данных.

public class Computerarithmetik {
    
    // Двоичное сложение
    public static String binaerAddition(String a, String b) {
        int laenge = Math.max(a.length(), b.length());
        
        // Дополнить нулями в начале
        a = String.format("%" + laenge + "s", a).replace(' ', '0');
        b = String.format("%" + laenge + "s", b).replace(' ', '0');
        
        StringBuilder ergebnis = new StringBuilder();
        int carry = 0;
        
        // Складывать справа налево
        for (int i = laenge - 1; i >= 0; i--) {
            int summe = carry + (a.charAt(i) - '0') + (b.charAt(i) - '0');
            ergebnis.append(summe % 2);
            carry = summe / 2;
        }
        
        // Добавить перенос в конце
        if (carry > 0) {
            ergebnis.append(carry);
        }
        
        return ergebnis.reverse().toString();
    }
    
    // Двоичное вычитание (метод дополнения до двух)
    public static String binaerSubtraktion(String a, String b) {
        // Инвертировать b (дополнение до двух)
        String negiert = zweierkomplementNegieren(b);
        
        // a + (-b)
        return binaerAddition(a, negiert);
    }
    
    private static String zweierkomplementNegieren(String binaer) {
        // Инвертировать биты
        StringBuilder invertiert = new StringBuilder();
        for (char bit : binaer.toCharArray()) {
            invertiert.append(bit == '0' ? '1' : '0');
        }
        
        // Добавить 1
        return binaerAddition(invertiert.toString(), "1");
    }
    
    // Двоичное умножение
    public static String binaerMultiplikation(String a, String b) {
        int aDez = Integer.parseInt(a, 2);
        int bDez = Integer.parseInt(b, 2);
        int produkt = aDez * bDez;
        
        return Integer.toBinaryString(produkt);
    }
    
    // Арифметика с фиксированной точкой
    public static double festePunktAddition(double a, double b, int nachkommastellen) {
        int faktor = (int) Math.pow(10, nachkommastellen);
        int aInt = (int) Math.round(a * faktor);
        int bInt = (int) Math.round(b * faktor);
        int ergebnisInt = aInt + bInt;
        
        return (double) ergebnisInt / faktor;
    }
    
    // Представление чисел с плавающей точкой (упрощённо)
    public static void gleitkommaDarstellung(double zahl) {
        if (zahl == 0) {
            System.out.println("0 = 0.0 × 2^0");
            return;
        }
        
        boolean negativ = zahl < 0;
        zahl = Math.abs(zahl);
        
        int exponent = 0;
        
        // Нормализовать
        while (zahl >= 2.0) {
            zahl /= 2.0;
            exponent++;
        }
        
        while (zahl < 1.0) {
            zahl *= 2.0;
            exponent--;
        }
        
        System.out.println((negativ ? "-" : "") + zahl + " × 2^" + exponent);
    }
    
    public static void main(String[] args) {
        System.out.println("=== Двоичная арифметика ===");
        
        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 summe = binaerAddition(a, b);
        System.out.println("a + b = " + summe + " (" + Integer.parseInt(summe, 2) + ")");
        
        String differenz = binaerSubtraktion(b, a);
        System.out.println("b - a = " + differenz + " (" + Integer.parseInt(differenz, 2) + ")");
        
        String produkt = binaerMultiplikation(a, b);
        System.out.println("a × b = " + produkt + " (" + Integer.parseInt(produkt, 2) + ")");
        
        System.out.println("\n=== Арифметика с фиксированной точкой ===");
        double x = 12.34;
        double y = 5.67;
        double summeFP = festePunktAddition(x, y, 2);
        System.out.println(x + " + " + y + " = " + summeFP + " (2 знака после запятой)");
        
        System.out.println("\n=== Представление чисел с плавающей точкой ===");
        double[] zahlen = {12.5, 0.75, -3.125, 256.0};
        for (double zahl : zahlen) {
            System.out.print(zahl + " = ");
            gleitkommaDarstellung(zahl);
        }
    }
}

5. Битовые операции и системы счисления в Python

# Python системы счисления и битовые операции

def dezimal_zu_binaer(dezimal):
    """Dezimal zu Binär umrechnen"""
    if dezimal == 0:
        return "0"
    
    binaer = ""
    while dezimal > 0:
        binaer = str(dezimal % 2) + binaer
        dezimal //= 2
    
    return binaer

def binaer_zu_dezimal(binaer):
    """Binär zu Dezimal umrechnen"""
    return int(binaer, 2)

def dezimal_zu_hex(dezimal):
    """Dezimal zu Hexadezimal umrechnen"""
    hex_ziffern = "0123456789ABCDEF"
    
    if dezimal == 0:
        return "0"
    
    hex = ""
    while dezimal > 0:
        hex = hex_ziffern[dezimal % 16] + hex
        dezimal //= 16
    
    return hex

def hex_zu_dezimal(hex):
    """Hexadezimal zu Dezimal umrechnen"""
    return int(hex, 16)

def bit_operationen_demo():
    """Bit-Operationen demonstrieren"""
    a = 12  # 1100
    b = 10  # 1010
    
    print(f"a = {a} (Binär: {bin(a)})")
    print(f"b = {b} (Binär: {bin(b)})")
    
    # Bitweises AND
    and_result = a & b
    print(f"a & b = {and_result} (Binär: {bin(and_result)})")
    
    # Bitweises OR
    or_result = a | b
    print(f"a | b = {or_result} (Binär: {bin(or_result)})")
    
    # Bitweises XOR
    xor_result = a ^ b
    print(f"a ^ b = {xor_result} (Binär: {bin(xor_result)})")
    
    # Bitweises NOT
    not_a = ~a
    print(f"~a = {not_a} (Binär: {bin(not_a & 0xFFFFFFFF)})")
    
    # Left Shift
    left_shift = a << 2
    print(f"a << 2 = {left_shift} (Binär: {bin(left_shift)})")
    
    # Right Shift
    right_shift = a >> 1
    print(f"a >> 1 = {right_shift} (Binär: {bin(right_shift)})")

def farbe_aus_rgb(rot, gruen, blau):
    """RGB-Farbe als 24-Bit-Wert zusammenfassen"""
    return (rot << 16) | (gruen << 8) | blau

def rgb_aus_farbe(farbe):
    """RGB-Komponenten aus 24-Bit-Farbe extrahieren"""
    rot = (farbe >> 16) & 0xFF
    gruen = (farbe >> 8) & 0xFF
    blau = farbe & 0xFF
    return rot, gruen, blau

def bit_manipulation_demo():
    """Bit-Manipulation demonstrieren"""
    zahl = 0b10101000  # 168
    
    print(f"Ursprüngliche Zahl: {zahl} (Binär: {bin(zahl)})")
    
    # Bit prüfen
    bit_position = 3
    bit_gesetzt = (zahl & (1 << bit_position)) != 0
    print(f"Bit {bit_position} gesetzt: {bit_gesetzt}")
    
    # Bit setzen
    mit_bit = zahl | (1 << bit_position)
    print(f"Setze Bit {bit_position}: {mit_bit} (Binär: {bin(mit_bit)})")
    
    # Bit löschen
    ohne_bit = zahl & ~(1 << bit_position)
    print(f"Lösche Bit {bit_position}: {ohne_bit} (Binär: {bin(ohne_bit)})")
    
    # Bit umschalten
    umgeschaltet = zahl ^ (1 << bit_position)
    print(f"Umschalte Bit {bit_position}: {umgeschaltet} (Binär: {bin(umgeschaltet)})")

def hauptprogramm():
    """Hauptprogramm mit allen Demonstrationen"""
    print("=== Zahlensysteme Umrechnung ===")
    
    zahl = 175
    print(f"Dezimal {zahl}:")
    print(f"  Binär: {dezimal_zu_binaer(zahl)}")
    print(f"  Hex: {dezimal_zu_hex(zahl)}")
    
    print("\n=== Bit-Operationen ===")
    bit_operationen_demo()
    
    print("\n=== Bit-Manipulation ===")
    bit_manipulation_demo()
    
    print("\n=== Farben als RGB ===")
    orange = farbe_aus_rgb(255, 107, 53)
    print(f"Orange: #{orange:06X}")
    
    r, g, b = rgb_aus_farbe(orange)
    print(f"RGB: ({r}, {g}, {b})")
    
    print("\n=== Zweierkomplement (8-Bit) ===")
    for i in range(-5, 6):
        if i >= 0:
            binaer = format(i, '08b')
        else:
            binaer = format((256 + i), '08b')
        print(f"{i:3d}: {binaer}")

if __name__ == "__main__":
    hauptprogramm()

Практические примеры

1. IP-адреса (IPv4)

IPv4-адреса состоят из 4 байтов (32 бита), обычно записываются в десятичной нотации:

Пример: 192.168.1.1

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

Binär: 11000000101010000000000100000001
Hex:   C0A80101

Маска подсети, пример: 255.255.255.0

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

2. MAC-адреса

MAC-адреса содержат 6 байтов (48 битов), обычно записываются в шестнадцатеричной нотации:

Пример: 00:1A:2B:3C:4D:5E

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

OUI (производитель): 00:1A:2B
ID устройства: 3C:4D:5E

3. Unicode и UTF-8

Символы Unicode представлены кодовыми точками, UTF-8 кодирует их в байты:

Пример: ‘A’ (U+0041)

Unicode: U+0041
Dezimal: 65
Binär:   01000001
UTF-8:   01000001 (1 Byte)

Пример: ’€’ (U+20AC)

Unicode: U+20AC
Dezimal: 8364
Binär:   00100000 10101100
UTF-8:   11100010 10000011 10101100 (3 Bytes)
Hex:     E2 82 AC

4. Цветовые коды (RGB)

Веб-цвета представлены 24-битными шестнадцатеричными значениями:

Пример: #FF6B35 (оранжевый)

#FF6B35
↓↓↓↓↓↓
Красный  Зелёный  Синий
FF       6B       35
↓        ↓        ↓
255      107      53

Binär: 11111111 01101011 00110101

Пример Java:

int orange = 0xFF6B35;
int rot = (orange >> 16) & 0xFF;   // 255
int gruen = (orange >> 8) & 0xFF;   // 107
int blau = orange & 0xFF;            // 53

5. Размеры файлов (двоичные vs десятичные)

Двоичные префиксы (IEC 80000-13):

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

Десятичные префиксы (SI):

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

Пример: жесткий диск 1 GB

Производитель (десятичная): 1,000,000,000 Bytes
Операционная система (двоичная): 1,000,000,000 ÷ 1,073,741,824 = 0.93 GB

6. ASCII-таблица (выборка)

СимволDezimalHexBinär
A654101000001
B664201000010
C674301000011
a976101100001
b986201100010
c996301100011
0483000110000
9573900111001

Интерактивные упражнения

Задачи на преобразование

Задача 1: десятичное в двоичное Преобразуйте следующие десятичные числа в двоичные:

  • 42
  • 127
  • 255
Показать решения
42₁₀ = 101010₂
127₁₀ = 1111111₂
255₁₀ = 11111111₂

Задача 2: двоичное в десятичное Преобразуйте следующие двоичные числа в десятичные:

  • 10101
  • 11110000
  • 10000000
Показать решения
10101₂ = 21₁₀
11110000₂ = 240₁₀
10000000₂ = 128₁₀

Задача 3: десятичное в шестнадцатеричное Преобразуйте следующие десятичные числа в шестнадцатеричные:

  • 100
  • 255
  • 4095
Показать решения
100₁₀ = 64₁₆
255₁₀ = FF₁₆
4095₁₀ = FFF₁₆

Задача 4: шестнадцатеричное в двоичное Преобразуйте следующие шестнадцатеричные числа в двоичные:

  • A5
  • FF
  • 1B
Показать решения
A5₁₆ = 10100101₂
FF₁₆ = 11111111₂
1B₁₆ = 00011011₂

Викторина по битовым операциям

Вопрос 1: Чему равно 12 & 10?

Показать решение
12₁₀ = 1100₂
10₁₀ = 1010₂
1100 & 1010 = 1000₂ = 8₁₀

Вопрос 2: Чему равно 12 | 10?

Показать решение
12₁₀ = 1100₂
10₁₀ = 1010₂
1100 | 1010 = 1110₂ = 14₁₀

Вопрос 3: Чему равно 12 ^ 10?

Показать решение
12₁₀ = 1100₂
10₁₀ = 1010₂
1100 ^ 1010 = 0110₂ = 6₁₀

Вопрос 4: Чему равно 8 << 2?

Показать решение
8₁₀ = 1000₂
1000 << 2 = 100000₂ = 32₁₀

Вопрос 5: Чему равно 16 >> 2?

Показать решение
16₁₀ = 10000₂
10000 >> 2 = 100₂ = 4₁₀

Задачи для самопроверки

Задача 6: Манипуляция битами Дано число 42 (101010₂). Установите 3-й бит (считая справа, с индексацией с 0).

Показать решение
42 = 101010₂
3-й бит справа = позиция бита 2
42 | (1 << 2) = 42 | 4 = 46₁₀ = 101110₂

Задача 7: Дополнительный код Представьте -5 в 8-битном дополнительном коде.

Показать решение
5₁₀ = 00000101₂
Инвертировать: 11111010₂
+ 1:           11111011₂

-5₁₀ = 11111011₂ (8-битный дополнительный код)

Задача 8: IP-адрес Преобразуйте IP-адрес 10.0.0.1 в шестнадцатеричный формат.

Показать решение
10.0.0.1
↓ ↓ ↓ ↓
0A 00 00 01

Hex: 0A000001

Типичные ошибки и подводные камни

1. Ошибки на единицу при индексации битов

Проблема: битовые позиции часто путают (индексация с 0 или с 1)

// ❌ Неправильно: битовая позиция 3 это 4-й бит справа
int bitPosition = 3;
boolean bitSet = (zahl & (1 << bitPosition)) != 0; // Проверяет бит 3 (4-й бит)

// ✅ Правильно: ясная документация индексации
// Битовая позиция 0 = крайний правый бит (LSB)
// Битовая позиция 7 = крайний левый бит в 8-битном числе
int bitPosition = 2; // 3-й бит справа (индексация с 0)
boolean bitSet = (zahl & (1 << bitPosition)) != 0;

2. Обработка знака при сдвиге

Проблема: правый сдвиг по-разному работает с числами со знаком и без знака

// Java: арифметический сдвиг вправо (>>) заполняет знаковый бит
int x = -8; // 11111111111111111111111111111000
int result = x >> 2; // 11111111111111111111111111111110 (-2)

// Логический сдвиг вправо (>>>) заполняет нулями
int x = -8;
int result = x >>> 2; // 00111111111111111111111111111110 (1073741822)

3. Проблемы переполнения

Проблема: переполнение целых чисел часто остаётся без внимания

// ❌ Без проверки переполнения
int max = Integer.MAX_VALUE; // 2147483647
int overflow = max + 1;     // -2147483648 (переполнение!)

// ✅ С проверкой переполнения
if (max == Integer.MAX_VALUE) {
    throw new ArithmeticException("Integer overflow");
}

4. Различные обозначения шестнадцатеричных чисел

Проблема: разные языки и контексты используют разные конвенции записи

// Различные обозначения:
0xFF      // Java/C префикс
0xFF      // C# префикс
0xFF      // JavaScript префикс
#FF       // CSS/Web
FFh       // Ассемблер
$FF       // Pascal

5. Забывают о порядке байтов

Проблема: игнорируют endianness при работе с байтами

// Прямой порядок байтов (x86, ARM): младший байт в начале
int wert = 0x12345678;
// В памяти: 78 56 34 12

// Обратный порядок байтов (сеть, PowerPC): старший байт в начале
// В памяти: 12 34 56 78

// Конвертация:
int littleEndian = 0x12345678;
int bigEndian = Integer.reverseBytes(littleEndian); // 0x78563412

6. Неправильное указание системы счисления при parseInt

Проблема: не указывают основание или указывают неверно

// ❌ Неправильно: парсит как десятичное число
int wert = Integer.parseInt("1010"); // 1010 (десятичное)

// ✅ Правильно: указываем основание
int wert = Integer.parseInt("1010", 2); // 10 (десятичное)
int hexWert = Integer.parseInt("FF", 16); // 255 (десятичное)

7. Выход за пределы диапазона дополнительного кода

Проблема: число не вмещается в указанную битовую ширину

// 8-битный дополнительный код: от -128 до 127
int wert = 200; // ❌ Выходит за пределы!

// Проверка диапазона:
if (wert < -128 || wert > 127) {
    throw new IllegalArgumentException("Значение выходит за пределы 8-битного диапазона");
}

8. Деление целого числа вместо вещественного

Проблема: целочисленное деление отбрасывает дробную часть

// ❌ Неправильно: целочисленное деление
int ergebnis = 5 / 2; // 2 (вместо 2.5)

// ✅ Правильно: деление вещественных чисел
double ergebnis = 5.0 / 2.0; // 2.5

9. Отрицание против побитового НЕ

Проблема: путают логическое отрицание и битовое инвертирование

// Логическое НЕ (!)
boolean a = true;
boolean b = !a; // false

// Побитовое НЕ (~)
int x = 5;      // 0101
int y = ~x;     // 1010 (в дополнительном коде: -6)

10. Ведущие нули в шестнадцатеричной записи

Проблема: ведущие нули влияют на значение в некоторых контекстах

// В Java: 0x0F = 15, 0xF = 15 (одинаково)
// Но при сравнении строк:
String a = "0F";
String b = "F";
// a.equals(b) = false!

Практические советы для разработчиков

1. Отладка с просмотром в двоичной и шестнадцатеричной системе

Используйте инструменты IDE:

// IntelliJ/Eclipse: двоичный просмотр в отладчике
// Переменная: 175
// Двоичный: 10101111
// Hex:      AF

Логирование в разных системах счисления:

System.out.println("Десятичное: " + zahl);
System.out.println("Двоичное: " + Integer.toBinaryString(zahl));
System.out.println("Шестнадцатеричное: " + Integer.toHexString(zahl));
System.out.println("Восьмеричное: " + Integer.toOctalString(zahl));

2. Битовые флаги в API

Эффективные флаги состояния:

// Определяем константы флагов
public static final int READ = 1 << 0;   // 0001
public static final int WRITE = 1 << 1;  // 0010
public static final int EXECUTE = 1 << 2; // 0100

// Комбинируем флаги
int permissions = READ | WRITE; // 0011

// Проверяем флаги
boolean canRead = (permissions & READ) != 0;
boolean canWrite = (permissions & WRITE) != 0;
boolean canExecute = (permissions & EXECUTE) != 0;

3. Преимущества битовых операций в производительности

Умножение и деление через сдвиги:

// Быстрее чем * 2
int x = 5;
int verdoppelt = x << 1; // 10

// Быстрее чем / 2
int halbiert = x >> 1; // 2

// Модуль 2^n через AND
int modulo8 = x & 0x7; // x % 8

⚠️ Важно: современные компиляторы часто оптимизируют это автоматически. Используйте сдвиги только в критичных местах.

4. Манипуляция цветовыми значениями

Эффективная работа с RGB:

// Создание цвета
int farbe = (255 << 16) | (107 << 8) | 53; // #FF6B35

// Извлечение компонентов
int rot = (farbe >> 16) & 0xFF;
int gruen = (farbe >> 8) & 0xFF;
int blau = farbe & 0xFF;

// Манипуляция цветом
int neueFarbe = farbe & 0x00FFFFFF; // Очистить красный компонент
neueFarbe |= (200 << 16); // Установить новое значение красного

5. Порядок байтов в сети

Обеспечение портативности:

// Host to Network Short (16-бит)
short hostPort = 8080;
short networkPort = Short.reverseBytes(hostPort);

// Network to Host Long (32-бит)
int networkAddress = 0xC0A80101; // 192.168.1.1
int hostAddress = Integer.reverseBytes(networkAddress);

6. Битовые маски для конфигурации

Компактное хранение конфигурации:

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. Экономная структура данных

Биты для булевых значений:

// Вместо 8 булевых значений (8 байт):
// boolean[] flags = new boolean[8];

// Один int (4 байта) для 8 флагов:
int flags = 0;
flags |= (1 << 3); // Установить 3-й флаг

8. Хеш-функции с битовыми операциями

Простая хеш-функция:

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. Вычисление чётности

Бит чётности для обнаружения ошибок:

public static boolean calculateParity(int x) {
    boolean parity = false;
    while (x != 0) {
        parity = !parity;
        x = x & (x - 1); // Удаляет младший установленный бит
    }
    return parity;
}

10. Проверка степени двойки

Эффективная проверка на степень двойки:

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

// Примеры:
// isPowerOfTwo(1) = true  (2⁰)
// isPowerOfTwo(2) = true  (2¹)
// isPowerOfTwo(4) = true  (2²)
// isPowerOfTwo(8) = true  (2³)
// isPowerOfTwo(6) = false

Продвинутые темы

1. BCD (Binary Coded Decimal)

BCD кодирует каждую десятичную цифру в 4 бита:

Пример: 42 в BCD

4      2
↓      ↓
0100   0010

BCD: 01000010

Преимущества:

  • Простое преобразование в десятичную и из десятичной
  • Точная десятичная арифметика

Недостатки:

  • Неэффективное использование памяти (6 из 16 значений не используются)
  • Более сложная арифметика

Реализация на 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. Gray-Code

Gray-Code это двоичная система счисления, где последовательные значения отличаются только одним битом:

Таблица Gray-Code (4 бита):

Десятичное | Двоичное | Gray-Code
-----------|----------|----------
0          | 0000     | 0000
1          | 0001     | 0001
2          | 0010     | 0011
3          | 0011     | 0010
4          | 0100     | 0110
5          | 0101     | 0111
6          | 0110     | 0101
7          | 0111     | 0100

Двоичный в Gray-Code:

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

Gray-Code в двоичный:

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

Применение: Роторные энкодеры, минимизация ошибок при переключении

3. CRC (Cyclic Redundancy Check)

CRC это метод обнаружения ошибок при передаче данных:

Простая реализация 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;
    }
}

Использование:

  • Сетевые протоколы (Ethernet, USB)
  • Проверка целостности файлов
  • Обнаружение ошибок в памяти

4. IEEE 754 Формат чисел с плавающей точкой

Стандарт для представления вещественных чисел:

32-битная одиночная точность:

Бит 31:     Знак (S)
Биты 30-23: Экспонента (E, 8 бит)
Биты 22-0:  Мантисса (M, 23 бита)

Значение = (-1)^S × 2^(E-127) × (1 + M)

Пример: 12.5 в IEEE 754

12.5 = 1100.1₂ = 1.1001₂ × 2³

S = 0 (положительное)
E = 127 + 3 = 130 = 10000010₂
M = 10010000000000000000000

IEEE 754: 01000001010010000000000000000000
Hex:      41480000

Реализация на 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)

Big Endian: Старший байт первым (сетевой порядок байтов) Little Endian: Младший байт первым (x86, ARM)

Пример: 0x12345678

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

Преобразование на Java:

public class Endianness {
    // Little Endian в Big Endian
    public static int littleToBig(int value) {
        return Integer.reverseBytes(value);
    }
    
    // Big Endian в Little Endian
    public static int bigToLittle(int value) {
        return Integer.reverseBytes(value);
    }
    
    // Проверка порядка байтов системы
    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
    }
}

Важно при работе с:

  • Сетевыми протоколами (TCP/IP использует Big Endian)
  • Форматами файлов
  • Кроссплатформенной разработкой

6. Бит чётности

Простой метод обнаружения ошибок:

Even Parity (чётная чётность):

Данные: 1011
Чётность: 0 (чтобы количество 1s было чётным: 10110 → 3 единицы = нечётное ❌)
Чётность: 1 (чтобы количество 1s было чётным: 10111 → 4 единицы = чётное ✅)

Реализация на 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); // Бит чётности = 0
        } else {
            return (byte) (data | 0x80); // Бит чётности = 1
        }
    }
}

7. Контрольные суммы

Простые контрольные суммы для проверки целостности данных:

Простая контрольная сумма:

public class Checksum {
    public static int calculate(byte[] data) {
        int sum = 0;
        for (byte b : data) {
            sum += (b & 0xFF);
        }
        return sum & 0xFFFF; // 16-битная контрольная сумма
    }
    
    public static boolean verify(byte[] data, int checksum) {
        return calculate(data) == checksum;
    }
}

8. Base64-кодирование

Кодирование двоичных данных в ASCII-символы:

Пример:

Двоичное: 01000001 01000010 01000011 (ABC)
Base64: QUJD

Реализация на 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. Ротация битов

Ротация без потери битов:

Левая ротация:

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

Правая ротация:

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

Пример:

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

10. Расстояние Хэмминга

Количество различающихся битов между двумя значениями:

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

// Пример:
// hammingDistance(0b1010, 0b1100) = 2
// 1010 ^ 1100 = 0110 (2 единицы)

Применение: коды исправления ошибок, криптография

Таблица преобразований

ДесятичноеДвоичноеШестнадцатеричноеВосьмеричное
0000000
1000111
2001022
3001133
4010044
5010155
6011066
7011177
81000810
91001911
101010A12
111011B13
121100C14
131101D15
141110E16
151111F17

Обзор битовых операций

Bit-Operationen

| Оператор | Символ | Описание | Пример |
|----------|--------|-------------|----------|
| AND | & | Побитовое И | 5 & 3 = 1 |
| OR | \| | Побитовое ИЛИ | `5 \| 3 = 7` |
| XOR | ^ | Исключающее ИЛИ | `5 ^ 3 = 6` |
| NOT | ~ | Побитовое НЕ | `~5 = -6` |
| Left Shift | << | Сдвиг влево | `5 << 2 = 20` |
| Right Shift | >> | Сдвиг вправо | `5 >> 1 = 2` |
| Unsigned Right Shift | >>> | Беззнаковый сдвиг вправо | `5 >>> 1 = 2` |

Размеры памяти

ЕдиницаБайтыБитыДиапазон (беззнаковый)
Byte180 - 255
Word2160 - 65,535
DWord4320 - 4,294,967,295
QWord8640 - 18,446,744,073,709,551,615

Диапазон прямого кода со знаком

БитыМинимумМаксимум
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

Преимущества и недостатки

Преимущества двоичной системы

  • Простота: только два состояния (0 и 1)
  • Надежность: легко реализуется без ошибок
  • Эффективность: оптимальна для электронных схем
  • Универсальность: основа всех цифровых систем

Преимущества шестнадцатеричной системы

  • Компактность: 4 двоичных цифры = 1 шестнадцатеричная цифра
  • Читаемость: короче и понятнее, чем двоичная
  • Стандартизация: широко распространена в программировании
  • Практичность: идеальна для адресов и цветов

Недостатки

  • Абстрактность: интуитивно непонятна для человека
  • Преобразования: требуют арифметических вычислений в уме
  • Ошибки: легко ошибиться при ручном преобразовании

Часто встречаемые экзаменационные вопросы

1. **Преобразуйте 175 (десятичное) в двоичное и шестнадцатеричное!**
   175₁₀ = 10101111= AF₁₆

2. **Каков результат 12 & 10 (побитовое И)?**
   12₁₀ = 1100₂, 10₁₀ = 1010₂ → 1100 & 1010 = 1000= 8₁₀

3. **Объясните прямой код со знаком!**
   Метод представления отрицательных чисел в двоичной системе путем инверсии битов и добавления 1.

4. **Для чего используются шестнадцатеричные числа?**
   Компактное представление двоичных значений, цветов, адресов памяти, кодов ошибок.

Основные источники

  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
Назад к блогу
Share:

Похожие статьи