Почему 0.1 + 0.2 не равно 0.3: как ПК хранит дробные числа?
Почему 0.1 + 0.2 не равно 0.3: как ПК хранит дробные числа?
Компьютер не ошибается, складывая 0.1 и 0.2. Проблема в том, что многие десятичные дроби невозможно точно записать в двоичной системе. Разбираемся без сложной математики.
Почему 0.1 + 0.2 не равно 0.3?
Откройте консоль браузера и выполните простое сложение:
0.1 + 0.2 // 0.30000000000000004
Выглядит так, будто JavaScript не справился с арифметикой начальной школы. Но язык здесь ни при чём. Похожий результат можно получить в Python, Java, C++ и многих других языках.
Компьютер посчитал всё правильно. Просто значения 0.1 и 0.2 изначально хранились с крошечной погрешностью.
Дело в двоичной системе
Мы привыкли записывать числа в десятичной системе. В ней дробь 0.1 выглядит идеально: одна цифра после запятой, никакой неоднозначности.
Компьютер хранит данные с помощью битов, поэтому дробные числа обычно переводятся в двоичную систему. И здесь начинается проблема: десятичное значение 0.1 невозможно представить конечным количеством двоичных цифр.
Получается бесконечная последовательность, похожая на привычную дробь 1 / 3 = 0.333333…. Мы не можем записать все тройки и вынуждены остановиться на каком-то месте. Компьютер делает то же самое с 0.1, только в двоичной системе.
Что на самом деле хранится в памяти
Большинство языков использует стандарт IEEE 754. Обычное 64-битное число с плавающей точкой состоит из знака, порядка и мантиссы. Мантисса хранит значимые цифры, а порядок определяет положение двоичной точки.
Места ограничены, поэтому бесконечную дробь приходится округлять до ближайшего доступного значения. Вместо точного 0.1 в памяти оказывается число примерно такого вида:
0.10000000000000000555…
У 0.2 тоже появляется небольшое отклонение. После сложения погрешности складываются, и ближайшим представимым результатом становится 0.30000000000000004.
При обычном выводе лишние цифры часто скрываются. Но если результат нельзя красиво сократить без потери информации, язык показывает его полностью.
Погрешность мала, но сравнение ломается
Разница настолько мала, что для большинства бытовых расчётов она ничего не меняет. Однако прямое сравнение может неожиданно вернуть false:
0.1 + 0.2 === 0.3 // false
Поэтому дробные результаты обычно сравнивают с допустимой погрешностью:
const result = 0.1 + 0.2; const expected = 0.3; const epsilon = 1e-10; Math.abs(result — expected) < epsilon; // true
Здесь мы спрашиваем не «совпадают ли числа до последнего бита», а «достаточно ли они близки для нашей задачи».
Правда, брать одно и то же значение epsilon повсюду тоже не стоит. Допустимая ошибка зависит от масштаба чисел и смысла расчёта. Требования к координатам элемента интерфейса и к научной модели будут разными.
С деньгами лучше не рисковать
Особенно неприятно встретить такую погрешность в расчёте цен, налогов и скидок. Округление результата только перед выводом не всегда спасает: ошибка может накопиться раньше и повлиять на условие, итоговую сумму или распределение платежей.
Для денег часто используют целые минимальные единицы. Вместо 19.99 доллара программа хранит 1999 центов. Целые числа представляются точно, пока не выходят за поддерживаемый диапазон.
const priceCents = 1999; const quantity = 3; const totalCents = priceCents * quantity; console.log(totalCents / 100); // 59.97
Другой вариант — специальные десятичные типы и библиотеки вроде Decimal. Они нужны там, где точность важнее скорости и удобства обычных чисел с плавающей точкой.
Почему тогда не хранить все дроби точно?
Можно, но за точность приходится платить памятью и скоростью. Числа с плавающей точкой удобны тем, что компактно описывают огромный диапазон значений и быстро обрабатываются процессором.
Для графики, игр, физики, машинного обучения и большинства инженерных вычислений небольшая погрешность вполне приемлема. Там важнее скорость миллионов операций, а не абсолютная точность каждой дроби.
Ошибка начинается не в формате числа, а в неверных ожиданиях разработчика. Если задача допускает приближение, обычный float подходит отлично. Если требуется точное количество денег, голосов или товаров, лучше выбрать другое представление.
Компьютер не ошибся
Результат 0.30000000000000004 выглядит странно только потому, что мы видим двоичную погрешность в десятичной записи. Сам компьютер последовательно работает с теми значениями, которые смог разместить в памяти.
Поэтому пытаться «исправить JavaScript» бессмысленно. Гораздо полезнее заранее решить, какая точность нужна программе, как сравнивать результаты и в каком формате хранить критичные данные.
И тогда загадка 0.1 + 0.2 перестаёт быть багом. Это обычный компромисс между точностью, диапазоном чисел и скоростью вычислений.
Оцените материал:
Похожие записи
Индийская компания EloElo привлекла 80 миллионов долларов от NBCUniversal, сделав ставку на растущий рынок микросериалов в стране.
04.11.2026
Компания, созданная на базе Массачусетского технологического института, превращает пластиковые отходы в прочные строительные материалы.
04.11.2026
Claude Ultra за 200 долларов: пятичасовой лимит сняли, но не всем
04.11.2026Присоединяйтесь и подпишитесь на рассылку самых свежих новостей по Email
Получайте свежие новости и идеи на почту. Без спама — только самое интересное.
Нажимая «Подписаться», вы соглашаетесь с политикой конфиденциальности.
