§ 13. Представление чисел в компьютере
Привет! Давайте разберёмся с фундаментальным вопросом: как компьютер, который «понимает» только нули и единицы, работает с числами — и почему у него есть свои ограничения, которые иногда приводят к багам в играх или странным результатам в калькуляторах.
Цифровая алхимия: как абстрактные нули и единицы становятся понятными нам числами
Введение: Числа в цифровой вселенной
Представьте: вы сидите за компьютером, открываете калькулятор или таблицу Excel, вводите числа… А что происходит внутри? Как процессор «понимает» число 42 или −1969? И почему иногда 0.1 + 0.2 ≠ 0.3? Сегодня мы приоткроем эту дверь.
💡 Важно понять
Самым первым видом данных, с которыми начали работать компьютеры, были именно числа. ЭВМ первого поколения могли производить только математические расчёты (вычисления). Сегодня компьютеры работают с целыми и вещественными числами, и их представление в памяти осуществляется разными способами.
13.1. Представление целых чисел
Целые числа — это основа основ. Когда вы считаете количество лайков под постом, нумеруете элементы массива в коде, указываете ID игрока в игре — везде работают целые числа.
Восемь выключателей — восемь битов: каждая комбинация «вкл/выкл» создаёт уникальное число
🎯 Зачем вообще нужны целые числа в компьютере?
Целые числа используются в задачах экономического характера (количества акций, сотрудников, деталей), для обозначения даты и времени, для нумерации различных объектов: элементов массивов, записей в базах данных, машинных адресов.
Интересный парадокс: любое целое число можно представить как вещественное с нулевой дробной частью (например, 42 = 42.0). Но компьютер всё равно хранит их отдельно. Почему?
💾 Экономия памяти
Целые числа занимают меньше места
⚡ Скорость
Операции с целыми числами выполняются быстрее
🎯 Точность
С целыми числами можно делать деление нацело с остатком
📌 Ключевая идея
Для компьютерного представления целых чисел используется несколько различных способов, отличающихся количеством разрядов (под целые числа обычно отводится 8, 16, 32 или 64 разряда) и наличием или отсутствием знакового разряда.
Беззнаковое представление: только положительные
Представьте, что у вас есть игра, где количество очков никогда не бывает отрицательным. Зачем тратить один бит на знак? Именно для таких случаев существует беззнаковое представление.
🔧 Алгоритм прост до безобразия:
- Переводим число в двоичную систему счисления
- Дополняем слева нулями до нужного количества разрядов (обычно 8, 16, 32 или 64)
📊 Пример для 8-битного представления
Числа 130 и 39 в 8-битном представлении:
130₁₀ = 10000010₂
39₁₀ = 00100111₂
Контейнер памяти: когда числа больше не помещаются, они «переливаются через край» и теряются
⚠️ Критическое ограничение
В n-разрядной ячейке можно хранить числа только от 0 до 2ⁿ−1.
Как ты думаешь: почему максимальное значение для 8 бит именно 255? (Подсказка: 2⁸ − 1 = 256 − 1)
| Количество разрядов | Диапазон чисел |
|---|---|
| 8 бит | [0; 255] |
| 16 бит | [0; 65 535] |
| 32 бита | [0; 4 294 967 295] |
| 64 бита | [0; 18 446 744 073 709 551 615] |
Знаковое представление: когда нужны и плюсы, и минусы
Теперь усложним задачу. Что если нам нужны отрицательные числа? Например, температура в игре может упасть ниже нуля, или баланс счёта может стать отрицательным.
Число с «бейджем знака»: самый очевидный, но не самый удобный способ хранения положительных и отрицательных чисел
📝 Прямой код: интуитивно, но неудобно
Прямой код — это представление числа в привычной для человека форме «знак–величина», при которой старший разряд ячейки отводится под знак (0 — для положительных, 1 — для отрицательных чисел), а остальные разряды — под цифры числа.
📊 Пример для 8-битной ячейки
Прямые коды чисел 48 и −52:
+48₁₀ → 00110000₂
−52₁₀ → 10110100₂
❌ Проблема прямого кода
В прямом коде числа можно хранить, но выполнение арифметических операций затруднено — оно требует более сложной архитектуры центрального процессора, «умеющего» выполнять не только сложение, но и вычитание, а также «знающего» особый алгоритм обработки не имеющего «веса» знакового разряда.
Есть решение получше…
Дополнительный код: магия кольцевой арифметики
Представьте механический счётчик с тремя колёсиками (как на старых одометрах). Когда вы крутите его вперёд от 999, он переходит на 000 (переполнение). Если крутить назад от 000, он показывает 999, 998, 997…
Кольцо времени и чисел: как движение вперёд и назад создаёт положительные и отрицательные значения
💡 Ключевая идея
Числа 999, 998, 997 можно рассматривать как коды чисел −1, −2, −3!
Проверим:
001 + 999 = 1000 (единица переполнения теряется → 000) ✓
002 + 998 = 1000 → 000 ✓
003 + 997 = 1000 → 000 ✓
Работает! Это и есть суть дополнительного кода.
🔧 Алгоритм получения дополнительного кода для отрицательного числа
- Берём модуль числа, представляем прямым кодом в n двоичных разрядах
- Инвертируем все биты (все нули заменить единицами, а единицы — нулями)
- Прибавляем 1 к полученному представлению
Зеркало инверсии: каждый бит превращается в свою противоположность
📊 Пример: найдём 16-разрядный дополнительный код для −2017₁₀
| Прямой код |−2017₁₀| = 2017₁₀ | 0000011111100001₂ |
| Инвертирование | 1111100000011110₂ |
| Прибавление единицы | + 1 |
| Дополнительный код | 1111100000011111₂ |
Слияние потоков: вычитание превращается в сложение, и процессору не нужны отдельные механизмы
✨ Магия в действии: вычитание превращается в сложение!
Как известно, 48 − 2017 = −1969. Выполним эту операцию в 16-разрядных машинных кодах:
0000000000110000 (прямой код числа 48)
+ 1111100000011111 (дополнительный код числа −2017)
─────────────────
1111100001001111 (результат в дополнительном коде)
Чтобы расшифровать результат, применяем те же операции (инвертирование + 1) и получаем −1969. Работает!
🤔 Как ты думаешь?
Почему использование дополнительного кода позволяет свести операцию вычитания чисел к операции поразрядного сложения кодов этих чисел? Какие преимущества это даёт процессору?
Сравнение: Математика vs Компьютер
Фундаментальное различие, которое важно понимать
Бесконечная лестница математики против конечной пирамиды компьютера
📐 В математике
Множество целых чисел:
- Дискретно (состоит из отдельных элементов)
- Бесконечно
- Не ограничено
💻 В компьютере
Множество целых чисел:
- Дискретно
- Конечно
- Ограничено
⚠️ Важное следствие
Максимальное положительное число, которое можно записать в знаковом представлении в n разрядах, равно 2ⁿ⁻¹ − 1.
В математике множество целых чисел бесконечно.
Компьютер работает с ограниченным множеством целых чисел.
Ключевые выводы по целым числам
13.2. Представление вещественных чисел
Целые числа — это понятно и чётко. Но что делать с числами вроде π (3.14159…), √2 или даже просто 0.5? Здесь мы входим в совершенно другую территорию.
🤔 Парадокс для размышления
В математике между любыми двумя вещественными числами лежит бесконечное множество других вещественных чисел. Например, между 12.34 и 12.35 есть 12.341, 12.3412, 12.34123… и так до бесконечности.
Как компьютер с его конечной памятью может это представить?
Спойлер: никак. Компьютер может хранить только приближения большинства вещественных чисел.
Попытка измерить бесконечность: как дискретная машина пытается ухватить непрерывный мир
📊 Две формы записи вещественных чисел
В жизни мы чаще пользуемся естественной формой записи чисел, при которой число представляется последовательностью десятичных цифр со знаком плюс или минус, для разделения целой и дробной частей числа используется запятая.
Примеры: 12,34; 0,0056; −708,9
Экспоненциальная (научная) форма записи
Вспомните физику: скорость света ≈ 300 000 000 м/с обычно записывают как 3 × 10⁸. Это удобно!
📌 Определение
В экспоненциальной форме вещественное число a представляется как:
a = ± m · qᵖ
где:
- m — мантисса числа (значащие цифры)
- q — основание системы счисления
- p — порядок числа (показатель степени)
📊 Пример: число 47,8 см можно записать так
- 478 · 10⁻¹ см
- 47,8 · 10⁰ см
- 4,78 · 10¹ см
- 0,478 · 10² см
- 0,000478 · 10⁵ см
Все эти записи корректны, но какая "правильная"?
Стандартизация: все числа выстраиваются по единому формату для удобства хранения
Нормализованная форма: стандарт для компьютеров
Чтобы избежать неоднозначности, используется нормализованная форма
💡 Определение (стандарт IEEE 754)
Нормализованная запись отличного от нуля вещественного числа — это запись вида:
a = ± m · qᵖ
где:
- p — целое число (положительное, отрицательное или ноль)
- m — дробь, целая часть которой содержит одну значащую (ненулевую) цифру
- 1 ≤ m < q
Другими словами, перед точкой (запятой) должна стоять ровно одна ненулевая цифра.
Телескоп с двумя настройками: одна регулирует детали (мантисса), другая — масштаб (порядок)
📊 Примеры нормализации чисел
| Исходное число | Нормализованная форма |
|---|---|
| 31,415926 | 3,1415926 · 10¹ |
| 1000 | 1,0 · 10³ |
| 0,123456789 | 1,23456789 · 10⁻¹ |
| 0,00001078₈ | 1,078₈ · 10₈⁻⁵ |
| 1000,00012₂ | 1,00000012₂ · 10₂¹¹ |
| AB,CDEF₁₆ | A,BCDEF₁₆ · 10₁₆¹ |
Реальность: ограниченная точность
Давай смоделируем работу простого калькулятора с 10 знакоместами на дисплее
🔢 Устройство нашего калькулятора
- 6 знакомест отводится под мантиссу (одно знакоместо — под знак мантиссы, четыре — под цифры мантиссы, одно — под точку)
- 1 знакоместо отводится под символ «E»
- 3 знакоместа отводятся под порядок (одно — под знак порядка, два — под цифры порядка)
Пиксельная реальность: гладкая математическая кривая превращается в ступенчатое приближение в компьютере
🤔 Интересный эксперимент
Число 12,34 в таком калькуляторе будет представлено как: +1.234E+01
Число 12,35 будет представлено как: +1.235E+01
А что с числом 12,341?
Оно тоже отобразится как +1.234E+01, потому что для последних разрядов не хватает места!
⚠️ Критическое следствие
Это означает, что наш калькулятор не различает числа 12,34, 12,341, 12,3412 и так далее — все они округляются до 12,34.
Вот почему в компьютере:
0.1 + 0.2 == 0.3 # False!
0.1 + 0.2 # 0.30000000000000004
Эффект домино: маленькие ошибки округления накапливаются и приводят к заметным отклонениям
📌 Главный вывод
Получается, что точно мы можем представить в компьютере лишь некоторую конечную часть множества вещественных чисел, а остальные числа — лишь приближённо.
Таким образом, множество вещественных чисел, представляемых в компьютере, дискретно, конечно и ограничено.
Диапазон и ограничения вещественных чисел
Понимание границ компьютерной арифметики
Окно в числовую вселенную: от микроскопического до космического, но с границами видимости
📐 В математике
Множество вещественных чисел:
- Непрерывно
- Бесконечно
- Не ограничено
💻 В компьютере
Множество вещественных чисел:
- Дискретно
- Конечно
- Ограничено
Камни через реку: компьютер прыгает по отдельным значениям, математика течёт непрерывно
⚠️ Практические последствия
Диапазон вещественных чисел в памяти компьютера очень широк, но, тем не менее, ограничен. Множество вещественных чисел, которые могут быть представлены в компьютере, конечно.
- Точность ограничена количеством разрядов для мантиссы и порядка
- Большинство вещественных чисел хранятся приближённо
- Эффекты округления неизбежны и могут накапливаться
- Никогда не сравнивайте вещественные числа на точное равенство!
Самое главное
Ключевые идеи, которые важно запомнить
🤔 Проверь себя
Вопросы для самопроверки и размышлений
1. Представьте в восьмиразрядном формате прямые коды десятичных чисел: 64, 58, 72, −96
Подсказка: для положительных чисел старший бит = 0, для отрицательных = 1
2. Можно ли числа 43₁₆, 101010₂, 129₁₀ и −52₁₀ сохранить в однобайтовом формате?
Проверьте диапазоны для беззнакового и знакового представления
3. Как определяется диапазон представления в компьютере целых чисел без знака? Со знаком?
Вспомните формулы: для беззнаковых [0; 2ⁿ−1], для знаковых?
4. Почему множество целых чисел, представимых в памяти компьютера, дискретно, конечно и ограничено?
Подумайте о физических ограничениях разрядной сетки
5. Представьте в восьмиразрядном формате дополнительные коды двоичных чисел: +1010₂, −1001₂, −11₂, −11011₂
Алгоритм:
- Модуль числа в прямом коде
- Инвертирование
- Прибавить 1
6. Найдите десятичные эквиваленты чисел, представленных в прямом коде: 00000100, 00001001, 10000011, 10000110
Первый бит — знак (0 = плюс, 1 = минус), остальные — величина
7. Найдите десятичные эквиваленты чисел, представленных в дополнительном коде: 00000100, 11111001
Если старший бит = 1, примените обратное преобразование
8. Для хранения целого числа со знаком используется два байта. Сколько единиц содержит внутреннее представление числа −101 в прямом коде? В дополнительном коде?
Попробуйте выписать оба представления и посчитать единицы
9. Мысленный эксперимент: Что произойдёт, если к максимальному 8-битному беззнаковому числу (255) прибавить 1?
Сможешь объяснить это через аналогию со счётчиком?
10. Практическая задача: В игре счётчик здоровья использует 8-битное беззнаковое представление. Игрок имеет 10 HP и получает урон 15 HP. Что покажет счётчик?
Подсказка: подумайте про переполнение "в обратную сторону"
11. Запишите десятичные числа в нормализованной форме: 217,934; 75321; 10,0101; 200450
Перед точкой должна быть ровно одна ненулевая цифра
12. Сравните следующие числа: 318,4785 · 10⁹ и 3,184785 · 10¹¹; 218,4785 · 10⁻³ и 1847,85 · 10⁻⁴
Приведите к одинаковому порядку для сравнения
13. Чем ограничивается диапазон представимых в памяти компьютера вещественных чисел?
Подумайте о разрядах для мантиссы и порядка
14. Почему множество вещественных чисел, представимых в памяти компьютера, дискретно, конечно и ограничено?
Как конечное число разрядов влияет на представление непрерывного множества?
15. Попытайтесь самостоятельно сформулировать основные принципы представления данных в компьютере
Подумайте о:
- Дискретности представления
- Ограниченности разрядной сетки
- Конечности множества представимых значений
- Компромиссах между точностью, диапазоном и объёмом памяти
💡 Вопросы для размышления и обсуждения
Challenge-вопросы для тех, кто хочет глубже понять материал
🎮 Challenge 1: Баг в игре
Представь, что ты делаешь игру с системой прокачки персонажа. Опыт игрока может достигать миллиардов. Используешь ли ты для этого вещественные числа или целые? Обоснуй свой выбор.
💰 Challenge 2: Финансы
Почему в финансовых приложениях (банки, биржи) никогда не используют стандартные вещественные числа (float) для хранения денег? Какое решение применяется вместо этого?
🔄 Challenge 3: Дополнительный код
Сможешь придумать аналогию для объяснения дополнительного кода кому-то, кто младше? Как бы ты объяснил, почему это элегантное решение?
🌍 Challenge 4: Реальная жизнь
Где в реальной жизни (в приложениях, играх, программах) можно встретить применение беззнакового представления? А где обязательно нужно знаковое?
🎯 Практические эксперименты
Попробуй применить полученные знания на практике!
🧪 Эксперимент 1: Калькулятор
Открой калькулятор Python (или любой другой) и вычисли:
0.1 + 0.1 + 0.1 - 0.3
Почему результат не равен нулю? Объясни через концепцию ограниченной точности.
🔢 Эксперимент 2: Переполнение
В любом языке программирования попробуй сложить два очень больших целых числа. Что происходит при переполнении?
💻 Эксперимент 3: Типы данных
Изучи, какие типы данных для целых чисел есть в твоём любимом языке программирования. Какие диапазоны они поддерживают?
🎲 Эксперимент 4: Создай свой код
Попробуй написать функцию, которая переводит положительное десятичное число в двоичную систему и выводит его 8-битное представление.