Кодирование информации в компьютере: числа, текст, цвет и звук

Кодирование информации в компьютере: числа, текст, цвет и звук Полезное

Кодирование информации — это перевод любых данных (числа, буквы, цвет, звук) в последовательность бит, с которой умеет работать процессор. Компьютер физически различает только два состояния — условно «0» и «1», поэтому все, что он обрабатывает, рано или поздно сводится к битам и байтам. Разберем, как это устроено для разных типов данных: чисел, текста, цвета и звука.

Биты, байты и почему все сводится к двоичному коду

Бит — минимальная единица информации, одно из двух состояний. Байт — группа из 8 бит, отсюда 256 возможных комбинаций (2^8). Байта хватает, чтобы закодировать один символ латиницы или число от 0 до 255, но не хватает на дробное число или символ с диакритикой — для них берут несколько байт подряд.

Дальше все зависит от схемы кодирования — правила, по которому конкретной последовательности бит ставится в соответствие конкретное значение: число, буква, оттенок цвета или уровень громкости. Схема кодирования — это соглашение: одни и те же 8 бит 01000001 в схеме для целых чисел означают 65, а в текстовой схеме ASCII — букву «A». Смысл байта задается контекстом (типом данных), а не самими битами.

Кодирование чисел

Целые числа компьютер хранит в дополнительном коде (two’s complement) — это стандарт в подавляющем большинстве современных процессоров для знаковых целых. Его удобство: сложение и вычитание работают по одним и тем же схемам для положительных и отрицательных чисел, отдельная логика вычитания не нужна.

Пример на 8 битах: число 5 — это 00000001 побитово нет, верно 00000101. Чтобы получить -5, инвертируют все биты (11111010) и прибавляют 1: получается 11111011. Первый (старший) бит здесь играет роль знака: 0 — число неотрицательное, 1 — отрицательное, но остальные биты уже не читаются как обычное число.

Дробные числа кодируют иначе — по стандарту IEEE 754. Число разбивается на три части: знак (1 бит), порядок/экспонента (8 бит для float32, 11 бит для float64) и мантисса (23 или 52 бита). Из-за конечной длины мантиссы многие десятичные дроби (например, 0.1) не представляются в двоичном виде точно — отсюда известный эффект 0.1 + 0.2 != 0.3 в большинстве языков программирования.

Проверим это на Python 3.12:

n = -5
as_signed = n.to_bytes(1, byteorder="big", signed=True)
print(as_signed.hex())
fb

fb в шестнадцатеричном виде — это как раз 11111011 в двоичном, дополнительный код числа -5 на 8 битах.

Если забыть про знак и попросить закодировать -5 как беззнаковое число, Python сразу укажет на ошибку, а не молча выдаст мусор:

n = -5
n.to_bytes(1, byteorder="big", signed=False)
OverflowError: can't convert negative int to unsigned

Исправление — явно указать signed=True, как в первом примере: беззнаковый тип физически не может хранить отрицательные значения.

Теперь посмотрим на кодирование дробного числа по IEEE 754 (float32, 4 байта):

import struct

value = 0.1
packed = struct.pack(">f", value)
print(packed.hex())
3dcccccd

Это и есть 0.1 в формате IEEE 754 single precision: точное десятичное значение «не влезает» в 23 бита мантиссы, поэтому хранится округленное приближение.

Кодирование текста: набор символов, кодировка, кодовая точка, байты

Здесь путают четыре разных понятия, и стоит развести их сразу:

  • Набор символов (character set) — просто список символов, которые вообще можно закодировать (буквы, цифры, знаки препинания).
  • Кодированный набор символов — тот же список, но каждому символу присвоен номер, кодовая точка (code point). В Unicode кодовые точки записывают как U+XXXX.
  • Кодировка (encoding) — правило, по которому кодовая точка превращается в конкретную последовательность байт. Одна и та же кодовая точка в разных кодировках может занимать разное число байт.

Сквозная цепочка на одном символе: буква «А» (кириллическая) — это кодовая точка U+0410 в Unicode — в кодировке UTF-8 та же точка записывается двумя байтами D0 90. Символ, число и байты — три разных уровня одного и того же значения.

Ключевой стандарт для текста сегодня — Unicode: единая таблица, куда стремятся включить символы всех письменностей мира, эмодзи и служебные знаки. Unicode задает только кодовые точки; способ их упаковки в байты — отдельный вопрос, и здесь используются кодировки UTF-8, UTF-16 и UTF-32. Точное число кодовых точек и номер текущей версии стандарта на 2026 год — см. «Точки для фактчека».

Проверим цепочку «символ — код — байты» в коде:

s = "Аня"
code_points = [hex(ord(c)) for c in s]
utf8_bytes = s.encode("utf-8")

print(code_points)
print(utf8_bytes)
print(len(s), len(utf8_bytes))
['0x410', '0x43d', '0x44f']
b'\xd0\x90\xd0\xbd\xd1\x8f'
3 6

Три символа кириллицы дали три кодовые точки, но шесть байт в UTF-8: каждая кириллическая буква в этой кодировке занимает 2 байта, а не 1, как латиница. Отсюда практический вывод: длина строки в символах (len(s)) и ее размер в байтах (len(s.encode(...))) — разные величины, и для нелатинских алфавитов они почти всегда расходятся.

Кодовые страницы: когда кодировок для текста несколько

До того как Unicode стал повсеместным стандартом, для не-английских языков использовали однобайтные кодовые страницы: таблицы на 256 позиций (8 бит), где 128 первых совпадали с ASCII, а вторые 128 отводились под национальный алфавит. Для кириллицы таких таблиц было сразу несколько (например, Windows-1251 и KOI8-R), и один символ в них кодировался разными байтами.

Отсюда классическая проблема «кракозябр»: если файл сохранен в одной кодовой странице, а открыт в программе, которая читает его в другой, каждый байт интерпретируется неверно, и вместо кириллицы на экране появляется бессмысленный набор символов. Проблему полностью не устранили — она встречается и сегодня при работе со старыми файлами и базами данных, — но переход на UTF-8 как кодировку по умолчанию для веба и большинства новых систем сильно снизил ее частоту. Детальный разбор конкретных символьных кодировок (ASCII, Windows-1251, KOI8-R, UTF-8, UTF-16) — тема отдельного материала.

Кодирование цвета

Цвет на экране кодируют через модель RGB: три канала (красный, зеленый, синий), обычно по 8 бит на канал. Это дает 256 градаций на канал и 256^3 = 16 777 216 возможных цветов — так называемый режим true color. Значение канала от 0 (нет составляющей) до 255 (максимум).

На практике цвет часто записывают в шестнадцатеричном виде: #FF7F00 — это RGB (255, 127, 0), то есть насыщенный оранжевый. Здесь важно упрощение: 8 бит на канал — стандарт для обычных дисплеев и веба, но не единственный вариант. Профессиональная обработка фото и видео использует 10-12 бит на канал ради большего числа полутонов, а indexed-палитры (например, старые GIF) кодируют цвет 8 битами на пиксель целиком, ссылаясь на таблицу из 256 заранее заданных цветов.

Кодирование звука

Звук в реальном мире — непрерывный аналоговый сигнал (колебание давления воздуха), а компьютер хранит только дискретные числа. Аналого-цифровое преобразование берет «срезы» сигнала через равные промежутки времени (дискретизация) и округляет амплитуду каждого среза до ближайшего доступного уровня (квантование). Такой способ кодирования звука отсчетами амплитуды называется PCM (импульсно-кодовая модуляция).

Два параметра определяют качество и размер файла: частота дискретизации (сколько отсчетов в секунду, измеряется в Гц) и глубина (сколько бит на один отсчет). Формат аудио-CD — 44 100 Гц и 16 бит на отсчет; это соответствует теореме Найквиста-Котельникова, по которой для честного восстановления сигнала с максимальной частотой около 20 кГц (верхняя граница слуха человека) частота дискретизации должна быть больше ее вдвое.

Что кодируем и чем представляем: сводная таблица

Что кодируем Чем представляем Пример
Целое число Биты в дополнительном коде -5 -> 11111011 (8 бит)
Дробное число IEEE 754 (знак, порядок, мантисса) 0.1 -> 3D CC CC CD (float32)
Символ текста Кодовая точка Unicode -> байты в кодировке «А» -> U+0410 -> D0 90 (UTF-8)
Цвет пикселя Каналы RGB, обычно 8 бит на канал оранжевый -> #FF7F00
Звук Отсчеты амплитуды, PCM аудио-CD: 44 100 Гц, 16 бит

Выводы

  • Любые данные в компьютере в итоге сводятся к битам, но смысл конкретной последовательности бит задается схемой кодирования, а не самими битами.
  • Для целых чисел стандарт — дополнительный код, для дробных — IEEE 754 с неизбежной потерей точности из-за конечной мантиссы.
  • Для текста важно различать четыре уровня: набор символов, кодовую точку, кодировку и итоговые байты — это разные вещи.
  • Кодовые страницы (Windows-1251, KOI8-R и другие) — исторический источник «кракозябр»; UTF-8 закрывает эту проблему для новых систем.
  • Цвет кодируют каналами (обычно RGB 8 бит на канал), звук — отсчетами амплитуды по PCM с частотой дискретизации и глубиной как ключевыми параметрами.

Где применяется / связь с практикой

Понимание кодирования данных нужно не только в теории: от него зависит, почему файл «весит» именно столько байт, почему текст иногда превращается в кракозябры при смене кодировки и почему округление дробных чисел дает неожиданный результат. Это база системного программирования — работы с памятью, сетевыми протоколами, форматами файлов и низкоуровневой оптимизацией.

Освойте тему на практике

Если хочется разобраться в этом на практике — как процессор и память работают с числами, byte-массивами и представлением данных на уровне железа и ОС, — подойдет курс Системное программирование.

Освойте тему на практике

Также можно заглянуть на открытые уроки Otus — там разбирают похожие практические задачи в прямом эфире.

FAQ

Можно ли по размеру файла в байтах точно узнать, сколько в нем символов текста?
Нет, только для кодировок с фиксированной длиной символа (например, чистый ASCII или UTF-32). В UTF-8 разные символы занимают от 1 до 4 байт, поэтому число байт и число символов совпадают только для текста целиком на латинице без спецсимволов.

Что произойдет, если открыть текстовый файл не в той кодировке, в которой он был сохранен?
Каждый байт (или группа байт) будет прочитан по чужой таблице соответствий и превратится в другой символ — отсюда «кракозябры» или ошибка декодирования. Сами байты файла при этом не портятся, портится только их интерпретация.

Почему 0.1 + 0.2 не равно ровно 0.3 в большинстве языков программирования?
Потому что оба числа хранятся в формате IEEE 754 как приближения: 0.1 и 0.2 не представляются двоичной дробью конечной длины точно, а их сумма округляется еще раз. Это свойство формата чисел с плавающей точкой, а не ошибка конкретного языка.

OTUS Журнал