Почему компьютер считает в нулях и единицах
Внутри процессора числа хранят миллиарды крошечных переключателей — транзисторов, у каждого из которых всего два устойчивых состояния: ток идёт или ток не идёт. Различить два таких состояния электроника умеет очень надёжно, почти без ошибок, даже при помехах и колебаниях напряжения. Десять разных уровней напряжения, по одному на каждую десятичную цифру, различать настолько же надёжно уже не получится — небольшая помеха легко перепутает соседние уровни между собой. Именно поэтому в основе цифровой техники лежит двоичная система, а не привычная человеку десятичная: она выбрана ради надёжности электроники, а не ради удобства счёта.
Как из нулей и единиц получается обычное число
Каждый ноль или единица в цепочке называется битом, а группа из восьми битов — байтом. Двоичная запись числа устроена по тому же принципу, что и привычная десятичная, только вместо разрядов по десять здесь разряды по два. В десятичном числе 253 крайняя правая цифра означает единицы, следующая — десятки, следующая — сотни. В двоичном числе точно так же каждая позиция что-то весит, только вес каждого следующего разряда растёт вдвое, а не в десять раз: единицы, двойки, четвёрки, восьмёрки и так далее. Запись 1011 в этой системе означает восемь плюс ноль плюс два плюс один, то есть одиннадцать в привычной десятичной записи. Процессор оперирует именно такими цепочками битов, а перевод в удобные человеку цифры на экране берёт на себя программа.
Отрицательные числа и дополнительный код
Самый простой способ обозначить отрицательное число — выделить один бит под знак: ноль значит плюс, единица значит минус. У этого способа есть неудобная особенность: получаются два разных представления нуля, положительный и отрицательный, и электронной схеме приходится обрабатывать это отдельным случаем. Инженеры выбрали более практичное решение — дополнительный код. Чтобы получить отрицательное значение числа, все биты положительной записи инвертируют, а затем прибавляют единицу к результату. Такой приём даёт всего одно представление нуля и, что особенно ценно, позволяет складывать положительные и отрицательные числа одной и той же электронной схемой сложения, без отдельной логики для вычитания.
Сложение и что такое переполнение
Двоичное сложение работает так же, как обычное сложение столбиком, только перенос в следующий разряд случается уже при сумме два, а не при сумме десять. Проблема начинается, когда для хранения числа выделено ограниченное количество битов, а результат сложения в эту ширину не помещается. Такую ситуацию называют переполнением. Классический пример — восьмибитное число со знаком, где наибольшее допустимое положительное значение равно ста двадцати семи. Стоит прибавить к нему единицу, и результат, вместо ожидаемых ста двадцати восьми, резко перескакивает на минус сто двадцать восемь: старший бит, отвечающий за знак, переполняется первым, и число как будто перепрыгивает на противоположный конец диапазона.
Дробные числа — числа с плавающей точкой
Целые числа хранятся точно, а вот дробные устроены иначе — их записывают похоже на привычную научную запись вроде трёх целых четырнадцати сотых, умноженных на десять в какой-то степени. Число делят на две части: значащие цифры и степень, на которую эти цифры умножаются. Такое устройство называют числом с плавающей точкой, потому что точка как бы «плавает» между разрядами в зависимости от степени. Оно позволяет одним и тем же набором битов хранить и очень маленькие, и очень большие числа, но платит за это ограниченной точностью — под значащие цифры выделено конечное количество битов, и втиснуть в них абсолютно любую десятичную дробь без округления не получается.
Почему 0,1 плюс 0,2 не равно ровно 0,3
В десятичной системе треть невозможно записать конечным числом цифр после запятой — получается бесконечная 0,3333 и так далее, сколько ни продолжай. В двоичной системе похожая беда случается с гораздо более простыми на вид дробями, например с одной десятой. Дробь 0,1 в двоичной записи превращается в бесконечно повторяющуюся последовательность битов, которую компьютер обязан где-то оборвать из-за ограниченного числа разрядов. Хранится крохотное приближение одной десятой, чуть-чуть больше или чуть-чуть меньше истинного значения. То же самое происходит с 0,2. Когда компьютер складывает два таких приближения, результат оказывается числом, отличающимся от идеальных 0,3 на исчезающе малую долю — слишком маленькую для повседневной жизни, но заметную, если программа сравнивает результат с числом 0,3 буквально, символ в символ.
Почему это важно не только программистам
Эта особенность выходит далеко за рамки учебных примеров. Программы для банков и магазинов считают деньги в целых копейках или центах, а не в дробных рублях, именно чтобы избежать накопления крошечных ошибок округления при тысячах ежедневных операций. Переполнение целых чисел тоже не абстрактная угроза: в реальных программах, от игр до систем управления, забытая проверка границы диапазона не раз приводила к неожиданным сбоям, когда счётчик резко перескакивал в отрицательные значения. Понимание этих механизмов помогает не удивляться, увидев на экране калькулятора длинный хвост из девяток или нулей после запятой там, где по всем ожиданиям должен был получиться простой круглый результат.
- для денег и других точных сумм использовать целые единицы вроде копеек, а не дробные рубли
- дробные числа в программах сравнивать с небольшим допустимым отклонением вместо точного совпадения
- перед сложением больших чисел проверять, укладывается ли ожидаемый результат в отведённую ширину разрядов
Частые вопросы
Почему компьютер не считает сразу в привычной десятичной системе
Из соображений надёжности электроники, а не удобства для человека. Различить два устойчивых состояния — ток есть или тока нет — транзистор умеет почти без ошибок даже при помехах. Надёжно различить десять разных уровней напряжения для десяти десятичных цифр гораздо сложнее, поэтому вся современная цифровая техника построена на двоичной системе.
Что такое переполнение простыми словами
Это ситуация, когда результат вычисления не помещается в количество битов, отведённых под число. Значение резко перескакивает на противоположный край допустимого диапазона вместо ожидаемого плавного роста, и внешне это выглядит как внезапная и необъяснимая ошибка в расчёте.
Это ошибка, что 0,1 плюс 0,2 не равно ровно 0,3 на экране
Нет, это ожидаемое поведение, а не брак программы. Дробь 0,1 не записывается в двоичной системе конечным числом битов, поэтому хранится её приближение, и сложение двух таких приближений даёт результат, чуть отличающийся от идеального 0,3.
Как программисты обходят неточность дробных чисел
Для денег и других точных величин используют целые числа в самых мелких единицах — копейках или центах, а сложение и сравнение делают уже с ними. Там, где дробные числа всё же нужны, их сравнивают с небольшим допустимым отклонением от ожидаемого значения вместо буквального равенства.