8–11Информатика

Почему в компьютере 0,1 + 0,2 ≠ 0,3

Один из самых удивительных моментов для начинающего программиста: компьютер, который умеет считать миллиарды операций в секунду, ошибается в сложении десятых. В Python 0.1 + 0.2 даёт 0.30000000000000004. Это не ошибка языка, а следствие того, как компьютер хранит дробные числа. Разбираемся, откуда берётся погрешность и как писать программы, чтобы она не мешала.

Для 8–11 классов, информатика и программирование.

Проверьте сами

print(0.1 + 0.2) print(0.1 + 0.2 == 0.3)

Первая строка выведет 0.30000000000000004, а вторая False: компьютер считает, что 0,1 + 0,2 не равно 0,3. То же самое произойдёт в JavaScript, Java, C++ и почти любом другом языке. Причина не в программе, а в самом способе хранения чисел.

Дроби в двоичной системе

Вспомним десятичные дроби. Дробь 1/3 нельзя записать конечной десятичной дробью: 0,333… без конца. А 1/4 можно: 0,25. Всё зависит от знаменателя: конечная десятичная дробь получается, только если в знаменателе одни двойки и пятёрки, ведь 10 = 2 · 5.

В двоичной системе основание 2. Конечными получаются только дроби, у которых в знаменателе одни двойки: 1/2 = 0,1₂, 1/4 = 0,01₂, 3/8 = 0,011₂. А 1/10 в знаменателе содержит пятёрку, поэтому в двоичной системе это бесконечная периодическая дробь:

0,1₁₀ = 0,0001100110011…₂

Компьютер не может хранить бесконечно много цифр. Обычно на число отводится 64 бита, и дробь обрезается. Получается не ровно 0,1, а очень близкое к нему число. С 0,2 то же самое. Когда складываются два приближённых числа, их маленькие ошибки складываются, и результат оказывается не ровно 0,3.

Как переводить числа между системами, мы подробно разобрали в статье о системах счисления.

Числа с плавающей точкой

Дробные числа в компьютере хранятся в формате с плавающей точкой, его описывает международный стандарт IEEE 754, принятый в 1985 году. Число записывается как мантисса, то есть значащие цифры, и порядок, показатель степени двойки. Это похоже на научную запись: 6,02 · 10²³.

Такой формат позволяет хранить и огромные числа, и крошечные, но точность ограничена: около 15–17 значащих десятичных цифр. Для большинства задач этого хватает с огромным запасом. Расстояние до Луны так можно записать с точностью до долей миллиметра.

Задача

Какие из этих дробей хранятся в компьютере точно: 0,5; 0,1; 0,25; 0,75; 0,3; 0,125?

Точно хранятся дроби со знаменателем-степенью двойки.

0,5 = 1/2, 0,25 = 1/4, 0,75 = 3/4, 0,125 = 1/8: точно.

0,1 = 1/10 и 0,3 = 3/10: в знаменателе пятёрка, хранятся приближённо.

Ответ: точно: 0,5; 0,25; 0,75; 0,125.

Как с этим жить в программах

Не сравнивайте дробные числа через ==

Вместо точного равенства проверяйте, что числа достаточно близки:

a = 0.1 + 0.2 print(abs(a - 0.3) < 1e-9)

Программа выведет True. Здесь 1e-9 это одна миллиардная: допустимая погрешность. В Python есть и готовая функция math.isclose(a, 0.3), которая делает то же самое.

Округляйте при выводе

print(round(0.1 + 0.2, 2))

Выведет 0.3. Округление при показе пользователю скрывает хвост погрешности, но сами вычисления остаются приближёнными.

Деньги в копейках

Для денег приближённость недопустима: никто не хочет потерять копейку. Поэтому банковские программы хранят суммы в целых копейках или используют специальные десятичные типы. В Python есть модуль decimal, где 0.1 + 0.2 даёт ровно 0.3.

Когда погрешность опасна

Обычно погрешность незаметна, но если её накапливать миллионы раз, она вырастает. Известная история произошла в 1991 году во время войны в Персидском заливе. Система противоракетной обороны считала время в десятых долях секунды, и 0,1 хранилась в ней приближённо. После 100 часов непрерывной работы ошибка накопилась до трети секунды. Система промахнулась, и ракета попала в казарму, погибли 28 человек.

Поэтому в науке, медицине и технике программисты внимательно следят за точностью вычислений. Этим занимается отдельная область математики: численные методы.

Это пригодится на ЕГЭ

На ЕГЭ по информатике встречаются задачи, где программа работает с дробными числами: деление, корни, проценты. Если сравнивать результаты через ==, можно получить неверный ответ из-за погрешности. Надёжнее работать с целыми числами, где это возможно: вместо проверки x / 3 == 2 писать x == 6, вместо проверки, что корень из n целый, сравнивать квадрат целого числа с n.

Задача

Нужно проверить, является ли число n полным квадратом. Почему проверка через корень может подвести и как сделать надёжно?

Корень вычисляется приближённо: для очень больших n он может дать число вроде 99999999.99999999 вместо 100000000.

Надёжно: взять целый корень r = math.isqrt(n) и проверить r * r == n. Здесь все вычисления целые и точные.

Ответ: через целочисленный корень math.isqrt и проверку r * r == n.

Как ещё решать задания ЕГЭ кодом, рассказали в статье о переборе на Python.

Разберём на занятии

Занимаемся по всем школьным предметам с 4 по 11 класс: живые занятия, общая доска и домашка, которую проверяет тот же преподаватель.

Первое занятие бесплатное: можно посмотреть формат вместе с ребёнком и решить, подходит ли он.

Частые вопросы

Значит, компьютеру нельзя доверять вычисления?

Можно. Погрешность очень мала, около одной десятимиллионной доли процента. Для подавляющего большинства задач она не имеет значения. Важно только помнить о ней, когда сравниваете дробные числа или считаете деньги.

Почему в калькуляторе телефона 0,1 + 0,2 = 0,3?

Калькуляторы для людей считают в десятичной системе или округляют результат перед показом. Программисты делают так специально: пользователь не должен видеть странные хвосты из нулей и четвёрок. Таблицы вроде Excel тоже округляют при показе, хотя внутри хранят приближённое число.

А целые числа тоже хранятся с погрешностью?

Нет. Целые числа хранятся точно. А в Python целые числа ещё и без ограничения по размеру: можно посчитать 2 в степени 1000 без единой ошибки.