Почему 0,1 + 0,2 в компьютере не всегда равно 0,3
0,1 и 0,2 хранятся как двоичные приближения, поэтому сумма получает хвост.

0,1 и 0,2 хранятся как двоичные приближения, поэтому сумма получает хвост.

Дальше вы увидите точные значения, скрытые за короткими записями на экране, проследите два округления и сможете выбрать правильную проверку для измерений, денег и обычного калькулятора.
В статье вы узнаете:
Запись 0,1 означает одну десятую, то есть дробь 1/10. В десятичной системе она занимает один разряд после запятой. В двоичной системе каждый следующий разряд означает половину, четверть, восьмую, шестнадцатую долю и так далее. Собрать ровно одну десятую конечной суммой таких долей нельзя.
Двоичное разложение начинается так:
Группа 0011 повторяется бесконечно. Ситуация знакома по десятичной дроби 1/3 = 0,3333… Число существует точно, но позиционная запись с выбранным основанием не заканчивается.
Критерий можно сформулировать без перебора разрядов. Сократите обычную дробь p/q. В двоичной системе её запись конечна, только если знаменатель равен степени двойки:
Поэтому 0,5 = 1/2, 0,25 = 1/4 и 0,125 = 1/8 записываются точно и в десятичной, и в двоичной системе. У 0,1 знаменатель равен 10 = 2 × 5, у 0,2 знаменатель равен 5. Лишний множитель 5 заставляет двоичный хвост повторяться.
Основание меняет запись, а не математическое число. Эту разницу подробно объясняет статья о десятичной, двоичной и шестнадцатеричной системах. Для целых примеров можно перевести 10 в 1010 с помощью конвертера из десятичной системы в двоичную, а затем вернуть 1010 в 10 через обратный конвертер. Оба инструмента сейчас работают с целыми числами, поэтому дробь 0,1 в них вводить не нужно.
Число с плавающей точкой хранит значащую часть и отдельно её масштаб. Похожий приём используется в научной записи 6,02 × 10²³: коэффициент сообщает значащие цифры, степень переносит запятую. В binary64 основание масштаба равно двум.
Для обычного ненулевого значения модель можно записать так:
Здесь s задаёт знак, m содержит значащие двоичные разряды, e сдвигает двоичную точку. Из 64 бит один отведён знаку, 11 кодируют показатель степени, 52 хранят дробную часть значащего числа. У нормальных значений первый двоичный разряд подразумевается, поэтому рабочая точность составляет 53 бита.
Важнее не раскладка битов, а её следствие. Binary64 содержит конечный набор чисел. Между двумя соседними значениями лежит промежуток, куда попадают все остальные вещественные числа. При чтении десятичной записи программа выбирает ближайшую доступную точку.
Шаг сетки меняется с масштабом. Возле нуля представимые числа расположены густо, возле больших значений дальше друг от друга. Поэтому выражение «около 15–16 значащих десятичных цифр» описывает общий порядок, но не обещает одинаковое число знаков после запятой повсюду.
IEEE 754-2019 задаёт форматы, операции, направления округления и особые значения для двоичной и десятичной арифметики. Стандарт не требует хранить любую величину именно в binary64, но этот формат стал обычным выбором для типа двойной точности. [IEEE 754-2019]
Ошибка представления появляется, когда точное исходное число отсутствует среди значений выбранного формата. Сохраняется ближайший доступный сосед, а разница остаётся меньше одного шага сетки.
Десятичные литералы выглядят коротко, но в binary64 им соответствуют такие точные значения:
0,1 хранится как примерно 0,10000000000000000555.
0,2 хранится как примерно 0,20000000000000001110.
Литерал 0,3 хранится как примерно 0,29999999999999998890.
Первые два приближения чуть больше задуманных десятичных дробей. Их точная математическая сумма равна примерно 0,30000000000000001665. Такого значения в binary64 тоже нет, поэтому операция должна выбрать одного из двух соседей.
В этом конкретном примере точная сумма сохранённых операндов оказывается ровно посередине. Обычный режим IEEE 754 выбирает ближайшее значение, а при равном расстоянии вариант с чётным младшим разрядом. Получается binary64-число, точное десятичное значение которого начинается с 0,30000000000000004440.
Сохранённый литерал 0,3 лежит на соседней нижней точке. Расстояние между ними равно одному ULP, то есть одному шагу представимых чисел возле 0,3:
Именно поэтому строгая проверка равенства видит два разных значения. Разница ничтожна для бытового сложения, но побитовое представление binary64 уже не совпадает.
JavaScript использует для типа Number значения binary64, а сложение выполняет по правилам IEEE 754. Спецификация ECMAScript описывает выбор ближайшего результата и режим округления к чётному при точном равенстве расстояний. [ECMAScript 2026, Number::add]
Эффект можно воспроизвести в обычном калькуляторе eCalc. Наберите через точку 0.1 + 0.2 и нажмите знак равенства. Калькулятор считает через JavaScript Number и показывает 0.30000000000000004.
Показывать все точные десятичные цифры двоичного значения неудобно. Форматтер обычно ищет короткую строку, которая при обратном чтении восстановит то же машинное число. Поэтому сохранённое приближение одной десятой спокойно выводится как 0,1.
Для суммы короткой безопасной строкой становится 0.30000000000000004. Если интерфейс заранее оставляет два знака после запятой, пользователь увидит 0,30. Изменилось отображение, а не значение, с которым продолжит считать программа.
Отсюда разные ответы языков, калькуляторов и таблиц. Один инструмент показывает достаточно цифр для обратного восстановления, другой форматирует денежную сумму, третий использует десятичный тип. Одинаковая надпись на экране ещё не доказывает одинаковый способ хранения.
Округление вывода подходит для отчёта, чека и подписи на графике. Оно не исправляет условие в коде, если сравнение произошло раньше. Сначала программа могла решить, что два значения не равны, и только потом нарисовать оба как 0,30.
Конечная точность влияет и на порядок действий. В точной арифметике сложение ассоциативно, то есть скобки не меняют сумму. В арифметике с плавающей точкой промежуточный результат округляется после каждой операции, поэтому выражения (a + b) + c и a + (b + c) иногда расходятся. Ошибка 0,1 + 0,2 мала, но в длинной сумме или при вычитании близких больших чисел последствия могут стать заметнее.
Количество цифр на экране настраивает интерфейс. Для проверки алгоритма нужно знать числовой тип, порядок операций и момент округления.
Строгое равенство подходит для целых счётчиков, кодов, индексов и результатов, которые по определению должны совпасть точно. Измерения, координаты и многие численные расчёты уже содержат погрешность. Здесь полезнее спросить, достаточно ли близки два результата для текущей задачи.
Надёжная проверка сочетает абсолютный и относительный допуски:
Абсолютный допуск задаёт приемлемую разницу в единицах результата. Относительный масштабирует её вместе с величиной чисел. Около нуля нужен абсолютный допуск, потому что относительная доля от нуля тоже равна нулю.
Для учебного сравнения суммы с 0,3 абсолютный допуск 10−12 намного больше фактической разницы 5,55 × 10−17, поэтому значения можно считать близкими. Это не универсальная настройка. Для координаты станка, температуры, цены и результата научной модели допустимая ошибка определяется разными требованиями.
Подставлять один epsilon во все программы опасно. При числах порядка миллиарда слишком малый абсолютный порог бесполезен, при значениях около нуля один относительный порог не пропускает даже безобидный вычислительный шум. Допуск должен следовать из масштаба данных и смысла решения.
Есть и обратная ошибка: применять «почти равно» там, где требуется идентичность. Два номера заказа не становятся одинаковыми из-за близости, а остаток на счёте нельзя молча простить как шум. В таких задачах лучше выбрать точное представление данных.
Для суммы с фиксированными двумя знаками простой вариант хранит не рубли, а копейки. Цена 12,34 рубля превращается в целое 1234, цена 5,67 рубля в 567. Сложение даёт 1801 копейку, или 18,01 рубля, без двоичного хвоста.
Целые копейки не отменяют правила округления. Семь с половиной процентов от 199 рублей составляют 14,925 рубля, или 1492,5 копейки. Нужно заранее решить, округлять ли до ближайшей копейки, всегда вверх, к чётному или по отраслевому правилу, и делать это в правильный момент.
Десятичные типы хранят коэффициент и степень десятки. Они способны представить 0,1, 0,2 и 0,3 точно в пределах своей разрядности, поэтому подходят для бухгалтерских инвариантов и договорных сумм. Такие типы есть в разных языках и библиотеках, например Decimal и BigDecimal.
У десятичной арифметики тоже конечная точность. Деление 1 на 3 потребует округления, а слишком длинный результат может выйти за выбранный контекст. Тип решает проблему представления конечных десятичных дробей, но не превращает компьютер в бесконечную тетрадь.
При создании десятичного значения используйте строку «0.1» или целые минимальные единицы, если API языка это различает. Передача уже готового binary64-числа может перенести в десятичный объект весь скрытый хвост, от которого хотели избавиться.
Выбор зависит от вопроса. Для физического измерения обычно подходят binary64 и обоснованный допуск. Для отображения достаточно округлить финальную строку. Для денег, налогов и точных десятичных правил нужны целые единицы либо десятичный тип с явно заданным режимом округления. Тогда 0,1 + 0,2 перестаёт быть загадкой и становится обычным решением о формате данных.
Напишите своё мнение, комментарий или предложение.