Простая математика за алгоритмами перевода между десятичной и двоичной системами
Если поискать в интернете How to convert from decimal to binary, вы найдёте четыре простых алгоритма: два для целых чисел и два для дробей. Они приведены с примерами ниже, в первой части статьи. Но хотя знания самих алгоритмов почти всегда достаточно, я решил разобраться, почему они работают. Во второй части статьи объясняется совсем базовая математика, лежащая в основе каждого из них. Понимание её может помочь вам восстановить любой из алгоритмов, если вы вдруг его забудете. Настоятельно советую взять блокнот и ручку и выполнять операции вместе со мной — так математика запомнится лучше. Вот те четыре алгоритма с примерами, которые можно найти в сети.
Перевод десятичного целого в двоичное
Чтобы перевести целое число в двоичную систему, возьмите исходное число и разделите его на 2, отмечая частное и остаток. Продолжайте делить частное на 2, пока не получите частное, равное нулю. Затем просто выпишите остатки в обратном порядке.
Вот пример такого перевода на числе 12. Сначала поделим число на два, указывая частное и остаток:
Теперь остаётся выписать остатки в обратном порядке — 1100. Итак, 12 в десятичной системе представляется как 1100 в двоичной.
Перевод десятичной дроби в двоичную
Чтобы перевести дробь в двоичную систему, возьмите исходную дробь и умножьте её на 2, отмечая получившиеся целую и дробную части. Продолжайте умножать на 2, пока получившаяся дробная часть не станет равна нулю. Затем просто выпишите целые части из результатов каждого умножения.
Вот пример такого перевода на дроби 0.375.
Теперь просто выпишем получившуюся целую часть на каждом шаге — 0.011. Итак, 0.375 в десятичной системе представляется как 0.011 в двоичной.
Перевод двоичного целого в десятичное
Чтобы перевести двоичное целое в десятичное, начните слева. Возьмите текущий итог, умножьте его на два и прибавьте текущую цифру. Продолжайте, пока цифры не закончатся. Вот пример такого перевода на целом числе 1011.
Перевод двоичной дроби в десятичную
Чтобы перевести двоичную дробь в десятичную, начните справа с итога, равного 0. Возьмите текущий итог, прибавьте текущую цифру и разделите результат на 2. Продолжайте, пока цифры не закончатся. Вот пример такого перевода на дроби 0.1011. Деление на 2 я просто заменил умножением на 1/2.
Вот вам четыре простых алгоритма, которые позволяют переводить двоичные числа в десятичные и обратно.
Разложение числа по основанию q
Ключ к пониманию того, почему эти алгоритмы работают, — это разложение числа по основанию q. Целое число в любой системе счисления можно представить в следующем виде:
где N — целое число, x — цифра (от 0 до 9 для системы по основанию 10, 0 и 1 для системы по основанию 2), q — значение основания (10 для системы по основанию 10, 2 для системы по основанию 2).
Далее в статье эта форма называется разложением числа N по основанию q, или просто разложением по основанию q. Посмотрим, как оно выглядит для числа 12 в десятичной и двоичной системах:
Точно так же дробное число в любой системе счисления можно представить в следующем виде:
где N — дробь, x — цифра (от 0 до 9 для системы по основанию 10, 0 и 1 для системы по основанию 2), q — значение основания (10 для системы по основанию 10, 2 для системы по основанию 2).
Для числа 0.375 в десятичной и двоичной системах представление такое:
Перевод десятичного целого в двоичное
Как выясняется, эту форму разложения по основанию q можно использовать для перевода числа из десятичной системы в двоичную. Сделаем это для того же числа 12. Сначала представим, будто мы не знаем, как оно записывается в двоичной системе, и выпишем его, заменив неизвестные цифры на x:
Наша задача — найти все x. Посмотрим, что здесь можно сделать. Первое, что нужно заметить: все слагаемые, кроме последнего, будут чётными числами, потому что все они кратны двум. Пользуясь этим, мы можем вывести цифру для x0: если переводимое целое чётное, то x0 равен 0, а если нечётное — то x0 должен быть 1. У нас число 12, оно чётное, значит x0 равен нулю. Запишем это:
Дальше нужно найти значение x1. Поскольку все слагаемые от x1 до xN кратны двум, мы можем вынести 2 за скобку, чтобы выделить x1. Сделаем это:
Также легко увидеть, что сумма значений внутри скобок равна 6. Значит, наш первый шаг можно записать так:
Продолжим искать остальные x. Многочлен внутри скобок можно выписать отдельным выражением:
Применяя ту же логику, что и выше, видим, что x1 равен 0. Перепишем это и снова вынесем 2 за скобку:
Итак, наш второй шаг:
Теперь видна закономерность. Мы можем продолжать выносить 2 за скобку, пока частное не станет нулём. Пойдём по этой схеме и посмотрим, что получится.
Поскольку частное равно 1, остаётся лишь одно слагаемое, поэтому перепишем предыдущее выражение:
Итак, наш третий шаг:
В итоге получаем следующее:
Понятно, что x3 равен 1. Но поскольку для нашего алгоритма нужно частное, перепишем предыдущее выражение так, чтобы в нём было частное:
Так как мы получили частное 0, работать больше не с чем, и это был наш последний шаг. Выпишем его:
Итак, перевод закончен. Вот как он выглядит по шагам:
Теперь ясно, что остаток на каждом шаге соответствует значению x в соответствующей позиции: первый остаток соответствует первому x, второй остаток — второму x и так далее. Значит, число 12 в двоичной системе по описанному выше алгоритму представляется как 1100.
Напомню, что мы начинали с намерения показать, почему работает алгоритм с делением на 2. Возьмём описанные выше шаги и перенесём 2 в левую часть выражений:
Вот так и видно, как мы пришли к алгоритму, описанному в начале. Расчёты этих четырёх шагов можно также свести в одно представление, вот такое:
Убедитесь, что вы понимаете, как мы получили это представление — оно понадобится, когда мы будем разбирать, как работает алгоритм перевода из двоичной системы в десятичную.
Перевод десятичной дроби в двоичную
Чтобы показать, почему при переводе дробей в двоичную систему мы умножаем на 2 и берём целую часть, я тоже воспользуюсь формой разложения по основанию q — для дробей. Возьму дробное число 0.375 из первой части статьи. Как и с целой частью, представим, будто мы не знаем, как это число записывается в двоичной системе, и выпишем его, заменив неизвестные цифры на x:
Как и с целыми числами, наша задача — найти все x, выделяя их по одному. Посмотрим, как это сделать. Первое, что стоит заметить: отрицательные степени 2 дают нам дроби со знаменателем 2 в положительных степенях. Перепишем выражение выше:
Сразу очевидно, что в правой части выражения можно просто вынести 1/2 за скобку. Сделаем это:
а затем перенесём 1/2 в левую часть
Итак, мы выделили x1 и знаем, что он может быть либо 1, либо 0. Чтобы определить, какая это цифра, посмотрим на остальные слагаемые:
Подумаем, насколько большой может быть сумма этих чисел. Если максимальное значение цифр x равно 1, то можно просто заменить x единицами и записать сумму так:
Что ж, это геометрическая прогрессия из дробей, и сумма такой прогрессии лежит в границах [0 < сумма < 1], так что максимум, который эта сумма может дать, — это 1. Теперь снова посмотрим на наше выражение:
Теперь должно быть понятно: если правая часть меньше 1, то x1 не может быть равен 1, а значит он равен 0, тогда как остаток равен 0.75.
Это выглядит в точности как первый шаг алгоритма, приведённого в начале:
Возьмём дробную часть 0.75 и вынесем ещё одну 1/2, чтобы выделить x2:
и перенесём 1/2 влево:
Теперь, если x2 равен 0, то сумма правой части выражения не может быть больше 1, но левая часть равна 1.5, значит x2 должен быть 1, а остаток — 0.5. Выпишем это:
И это снова следует схеме алгоритма, приведённого в начале:
Повторим те же действия для оставшейся дробной части 0.5.
По той же логике, что и выше, видим, что x3 равен 1 и дробной части не остаётся:
Поскольку оставшаяся дробная часть равна 0, наш последний шаг выглядит так:
Выпишем ещё раз все шаги:
Это в точности тот алгоритм, который я привёл в начале. Как и с целыми числами, расчёты этих трёх шагов можно свести в одно представление:
И снова важно полностью ухватить это представление — оно понадобится при разборе перевода из двоичной системы в десятичную.
Почему не все дроби можно конечно представить в двоичной системе
То, что некоторые дроби, конечно представимые в десятичной системе, невозможно конечно представить в двоичной, для многих разработчиков оказывается неожиданностью. Но именно в этой путанице и лежит корень внешне странного результата сложения 0.1 и 0.2. Так что же определяет, можно ли конечно представить дробь в системе счисления? Чтобы число было представимо конечно, знаменатель дроби должен быть степенью основания системы. Например, для системы по основанию 10 знаменатель должен быть степенью 10 — вот почему мы можем конечно представить 0.625 в десятичной системе:
и не можем конечно представить 1/3:
То же самое и для системы по основанию 2:
Но если взять 0.1, то знаменатель равен 10, а это не степень 2, значит 0.1 в двоичной системе будет бесконечной дробью. Убедимся в этом с помощью алгоритма, который мы разобрали выше:
Так можно продолжать бесконечно, но давайте запишем это как периодическую дробь:
Перевод двоичного целого в десятичное
Я возьму то же двоичное целое 1011 из первого раздела, чтобы показать, почему работает алгоритм с умножением на 2. Здесь мы тоже воспользуемся формой разложения по основанию q. Запишем число в этом виде:
Поскольку все слагаемые кратны 2, мы можем выносить 2 за скобку, пока частное не станет нулём. Сделаем это:
Теперь, если просто соблюдать порядок математических операций, вы получите в точности те же шаги, что я показал в начале, а именно:
Таким образом, 1011 в двоичной системе — это 11 в десятичной.
Перевод двоичной дроби в десятичную
Вот мы и дошли до последнего алгоритма. Возможно, вы уже сами разобрались в его механике. Если нет — посмотрим, почему он работает. Форма разложения по основанию q и здесь оказывается ключом. Возьмём число 0.1011 из первого раздела. Запишем его в развёрнутом виде:
И снова, поскольку все слагаемые кратны 1/2, мы можем выносить 1/2 за скобку, пока не останется дробной части. Сделаем это:
Соблюдение порядка математических операций даёт алгоритм, описанный в начале:
Таким образом, 0.1011 в двоичной системе — это 0.6875 в десятичной.