Краткое содержание
Целое число в программе не есть целое число математики: это конечная последовательность битов, и в битах помещается ровно различных значений. Этот урок изучает такое представление и его следствия. Он учит записывать целое число в двоичной и шестнадцатеричной системах и проверять запись спецификациями формата {:b} и {:x}; представляет целочисленные типы C++, их размер, который измеряется с помощью sizeof, и их диапазон значений, который узнают с помощью std::numeric_limits; объясняет, как представляются отрицательные числа с помощью дополнительного кода и почему диапазон несимметричен; различает знаковые и беззнаковые целые и изучает, что происходит, когда операция выходит за пределы диапазона: для беззнаковых стандарт определяет результат, и это арифметика по модулю ; для знаковых не определяет, и программа лишается всяких гарантий. В конце дан критерий выбора типа для каждого значения.
Цели обучения
- Переводить целые числа между системами с основаниями 10, 2 и 16 и проверять перевод спецификациями формата
{:b}и{:x}. - Вычислять диапазон значений целого числа из битов, знакового и беззнакового, и проверять его с помощью
std::numeric_limitsиsizeof. - Объяснять дополнительный код и записывать представление небольшого отрицательного числа в восьми битах.
- Отличать знаковое переполнение, которое есть неопределённое поведение, от беззнакового переполнения, которое есть арифметика по модулю , и предсказывать результат второго.
- Выбирать подходящий целочисленный тип для значения по его ожидаемому диапазону, используя типы фиксированной ширины из
<cstdint>, когда ширина важна.
Позиционные системы счисления
Урок Переменные и память представил переменную как ячейку с именем, типом и значением. Однако память хранит не десятичные цифры, а биты, каждый из которых равен 0 или 1, сгруппированные в байты по восемь битов (стандарт требует не менее восьми; все современные системы используют ровно восемь). Чтобы знать, какие значения может принимать целочисленная переменная, нужно, следовательно, знать, как записать число всего двумя цифрами, и именно это делает позиционная система счисления с основанием 2.
В позиционной системе счисления с основанием каждая цифра весит в зависимости от занимаемой позиции: последняя умножается на , стоящая перед ней на , стоящая перед той на , и так для всех цифр. В десятичной системе означает . В двоичной системе цифры суть 0 и 1, а веса суть степени двойки: Отсюда следует правило перевода из двоичной системы в десятичную: складываются веса позиций, где стоит 1.
Рисунок читает восьмибитовую комбинацию 00101101. Над каждой ячейкой указан её вес; единицы зажигаются по одной, слева направо, и значение накапливает их веса.
Доказательство
Восемь ячеек, все погашены: значение равно 0. Веса, справа налево, равны .
Первая единица стоит в позиции с весом 32: накопленное значение равно 32.
Следующая, в позиции с весом 8: .
Следующая, в позиции с весом 4: .
Последняя, в позиции с весом 1: . Комбинация
00101101представляет число 45.Сгруппированные по четыре, биты дают шестнадцатеричные цифры:
0010есть 2, а1101есть 13, что в шестнадцатеричной системе записывается какd. Следовательно, 45 записывается в шестнадцатеричной системе как2d.
Обратный путь, из десятичной системы в двоичную, проходится последовательным делением: число делят на 2, записывают остаток (0 или 1) и повторяют с частным, пока не получится 0; остатки, прочитанные от последнего к первому, и есть двоичные цифры. Для 45: , , , , , ; прочитанные в обратном порядке, остатки дают 101101, что с двумя нулями слева есть комбинация с рисунка. Причина в том, что каждый остаток есть цифра единиц делимого числа, а каждое частное есть это число, сдвинутое на одну позицию вправо. Тот же метод годится для любого основания: для основания 16 делят на 16, а остатки от 10 до 15 записывают буквами от a до f.
Основание 16, или шестнадцатеричная система, существует ради удобства: так как , каждая шестнадцатеричная цифра соответствует ровно четырём битам, и байт записывается двумя цифрами. 32-битовое число занимает тридцать две цифры в двоичной записи, которая плохо читается, и всего восемь в шестнадцатеричной, которые переводятся в двоичную запись без вычислений, цифра за цифрой. Поэтому программисты читают память в шестнадцатеричной записи. Математическое обоснование позиционных систем изучается в курсе «Алгебра и геометрия I», в уроке «Позиционные системы счисления».
C++ допускает целые числа, записанные прямо в этих системах: литерал, начинающийся с 0b, записан в двоичной системе, а начинающийся с 0x, в шестнадцатеричной. Внутри любого литерала можно поставить апостроф как разделитель разрядов, который компилятор игнорирует, а читатель ценит: 0b0010'1101, 1'000'000. При выводе спецификация формата {:b} записывает целое число в двоичной системе, {:x} в шестнадцатеричной строчными буквами, а {:X} прописными; знак # добавляет префикс, а ширина, перед которой стоит ноль, дополняет число нулями слева.
#include <print>int main(){ const int decimal{45}; const int binary{0b0010'1101}; const int hexadecimal{0x2d}; std::println("{} {} {}", decimal, binary, hexadecimal); std::println("двоичная: {:b}", decimal); std::println("восемь битов: {:08b}", decimal); std::println("шестнадцатеричная: {:x} {:X} {:#x}", decimal, decimal, decimal);}45 45 45двоичная: 101101восемь битов: 00101101шестнадцатеричная: 2d 2D 0x2dТри переменные хранят одно и то же значение, записанное в трёх системах, и первая строка вывода это показывает: система счисления есть свойство записи, а не числа. Память во всех трёх случаях хранит одну и ту же битовую комбинацию. Спецификация {:b} опускает нули слева; {:08b} дополняет запись до восьми цифр.
Загрузка содержимого…