Арифметика и округления: ошибки, устойчивость, сравнение чисел
Зачем нужна эта тема
В предыдущих статьях курса мы разобрали, как числа кодируются:
целые в двоичном виде, включая дополнительный код и переполнение;
дробные в фиксированной точке, где масштаб задаётся вручную;
числа с плавающей точкой IEEE 754, где масштаб хранится в экспоненте и появляются особые значения вроде NaN и .Следующий шаг — понять, как именно происходят вычисления над этими кодами и почему результаты часто «чуть-чуть не те», даже если переполнения нет.
В этой статье разберём:
откуда берутся ошибки округления и какие они бывают;
что такое абсолютная и относительная ошибка;
почему одни формулы вычисляются надёжно, а другие нет (устойчивость);
как правильно сравнивать числа с плавающей точкой;
что делать с NaN, бесконечностями и нулями IEEE 754 в логике программ.!Иллюстрация неравномерного шага между соседними числами float по диапазону
Округление как неизбежность конечного формата
Что происходит при записи результата
Любой формат с фиксированным числом битов имеет конечное множество представимых значений. Если математический результат операции не попадает точно в это множество, он заменяется ближайшим представимым значением по правилу округления.
Для IEEE 754 наиболее распространённый режим по умолчанию — округление к ближайшему, при равенстве к чётному (round to nearest, ties to even). Идея «к чётному» нужна, чтобы избежать систематического смещения при большом числе округлений.
Модель ошибки округления
Для многих рассуждений удобно считать, что результат операции над вещественными числами получается так:
Пояснение элементов формулы:
— точный (математический) результат;
— результат после округления в формат (от слова floating);
— относительная ошибка округления; обычно она мала и ограничена некоторой константой, зависящей от формата и режима округления.Важный смысл: ошибка часто относительная, то есть масштабируется вместе с числом. Поэтому абсолютная ошибка у больших чисел обычно больше.
Абсолютная и относительная ошибка
Пусть есть истинное значение и приближённое .
Абсолютная ошибка
Пояснение:
— истинное значение;
— вычисленное или представимое значение;
— модуль;
показывает «на сколько в абсолютных единицах» мы ошиблись.Абсолютная ошибка полезна, когда известна физическая шкала: например, ошибка в метрах.
Относительная ошибка
Пояснение:
числитель — абсолютная ошибка;
знаменатель — масштаб истинного значения;
показывает, насколько ошибка велика по сравнению с самим числом.Относительная ошибка удобна, когда важен процент или число значащих цифр. Но она плохо определена, если (деление на очень маленькое число раздувает показатель).
ULP и машинный эпсилон
ULP
ULP (unit in the last place) — шаг между двумя соседними представимыми числами в конкретной области шкалы. В IEEE 754 этот шаг растёт с увеличением экспоненты: чем больше по модулю число, тем «реже сетка».
Практический смысл: ошибка «в 1 ULP» часто означает «округлено максимально аккуратно для данного формата», но абсолютный размер ULP зависит от величины числа.
Машинный эпсилон
Машинный эпсилон обычно понимают как расстояние между 1 и ближайшим числом больше 1 в данном формате.
для binary64 (double) это примерно ;
для binary32 (float) это примерно .Здесь показатель связан с тем, сколько бит точности хранится в значащей части (с учётом скрытой единицы у нормализованных чисел).
Важно: машинный эпсилон — это не «максимальная ошибка любых вычислений», а характеристика плотности представимых чисел около 1.
Типичные источники численных проблем
Катастрофическая потеря значимости
Самый известный случай — вычитание близких чисел:
если , то разность мала;
но в вычислении участвуют числа, у которых уже есть округления;
значащие биты результата могут «вымыться», и относительная ошибка результата резко возрастает.Пример идеи без привязки к конкретным значениям:
было две величины с точностью до, скажем, 15 значащих цифр;
после вычитания почти равных чисел может остаться 2–3 значащих цифры.Это не «баг IEEE 754», а математическое следствие: операция вычитания превращает относительную ошибку входов в большую относительную ошибку выхода.
Поглощение (absorption)
Если складывать очень большое и очень маленькое число, маленькое может не изменить сумму:
, если меньше половины ULP около .Это нормальное поведение округления. Особенно заметно при накоплении суммы, когда частичные суммы растут, а добавки остаются маленькими.
Накопление ошибки
Округление происходит почти после каждой операции. Ошибки могут:
частично компенсироваться;
или накапливаться, если порядок действий неудачен.Поэтому два математически эквивалентных выражения могут давать различный результат на компьютере.
Устойчивость алгоритмов
Важно различать:
чувствительность задачи: насколько сильно истинный результат меняется при малом изменении входа;
устойчивость алгоритма: насколько алгоритм добавляет ошибок сверх неизбежных.Идея устойчивости в практических терминах
Алгоритм считается численно устойчивым, если итог можно интерпретировать как точный ответ для немного изменённых входных данных, где «немного» сопоставимо с машинными округлениями.
Эта формулировка полезна, потому что:
любое представление входа уже содержит округление;
«идеальной точности» всё равно нет.Пример: суммирование многих чисел
При сумме большого количества слагаемых порядок сложения влияет на результат.
Практические правила:
складывать числа примерно одного масштаба обычно лучше;
суммировать от меньших по модулю к большим часто точнее, чем наоборот;
для высокой точности используют компенсированное суммирование.Один из известных приёмов — суммирование Кэхэна (Kahan summation): оно хранит компенсацию потерянной «мелочи» и уменьшает ошибку накопления.
В этом курсе важно понимать когда нужно думать о таких приёмах: когда вы складываете много чисел с разными масштабами (статистика, интегрирование, обработка сигналов).
Сравнение чисел с плавающей точкой
Почему a == b часто плохая идея
Из-за округлений выражения, которые математически равны, могут давать результаты, отличающиеся на малую величину.
Пример типовой ситуации:
вы вычисляете значение двумя способами (через разные формулы);
оба способа «правильные», но округления разные;
прямое сравнение на равенство возвращает ложь.Сравнение с допуском
Обычно используют допуск.
Абсолютный допуск:
Пояснение:
, — сравниваемые значения;
— их абсолютная разница;
— выбранный порог (например, допустимая физическая погрешность измерения).Относительный допуск:
Пояснение:
числитель — абсолютная разница;
знаменатель — масштаб сравниваемых значений (берём максимум, чтобы не делить на слишком маленькое);
— допустимая относительная ошибка.На практике часто комбинируют оба:
абсолютный допуск важен около нуля;
относительный — на больших масштабах.Важная оговорка про выбор допуска
Допуск нельзя выбирать «магически» одинаковым для всех задач. Его определяют:
требования предметной области (например, точность датчика);
масштаб данных;
оценка накопленной ошибки алгоритма.Особые значения IEEE 754 в вычислениях и сравнениях
NaN
NaN распространяется через многие операции и «ломает» обычные сравнения:
— ложь для любого ;
и — тоже ложь;
— истина для любого , включая NaN.Вывод: проверять NaN нужно специальными средствами языка или библиотеки.
Бесконечности
и ведут себя предсказуемо во многих операциях (например, сравнения с конечными числами), но требуют аккуратности в выражениях вида , которые дают NaN.
Два нуля
В IEEE 754 есть и . Обычно они равны в сравнении, но могут различаться в выражениях вида , давая . Это полезно для корректной обработки предельных переходов.
Практические рекомендации
Избегайте вычитания близких чисел, если можно преобразовать формулу.
При суммировании большого количества чисел учитывайте порядок сложения; при необходимости используйте компенсированное суммирование.
Не сравнивайте числа с плавающей точкой через прямое равенство, кроме случаев, когда вы точно контролируете вычисления (например, сравнение с заранее записанной константой, которая получена тем же способом).
Для сравнения используйте абсолютный и относительный допуски.
Всегда продумывайте, что должно происходить при NaN и в вашей логике.Что дальше
Эта статья завершает базовую картину курса: мы не только знаем, как числа кодируются, но и понимаем, как вычисления над кодами порождают округления и ошибки, и как с этим жить в инженерной практике.
Если углубляться далее, обычно рассматривают:
оценку ошибки конкретных численных методов;
ULP-метрики и тестирование корректности библиотек;
интервальные вычисления и рациональную арифметику в задачах, где ошибки недопустимы.Источники для углубления
What Every Computer Scientist Should Know About Floating-Point Arithmetic (David Goldberg)
Wikipedia: Floating-point arithmetic
Wikipedia: Machine epsilon
Wikipedia: Kahan summation algorithm
Wikipedia: Numerical stability