Числа с плавающей запятой недействительны Чак Эллисон

Числа с плавающей запятой не являются «действительными числами» в математическом смысле, хотя в некоторых языках программирования, например в Pascal и Fortran, носят название таковых (real). Действительные числа имеют бесконечную точность, и потому они непрерывны и не подвержены искажениям. Числа с плавающей запятой имеют ограниченную точность, а потому конечны и похожи на «непослушные» целые, так как неравномерно распределены по всему своему диапазону.

Чтобы проиллюстрировать это, попробуйте присвоить 2147483647 (самое большое 32-разрядное целое со знаком) 32-разрядной переменной типа float (скажем, x), а потом напечатайте его. Вы увидите 2147483648. Теперь напечатайте x - 64. Результат — снова 2147483648. Теперь выведите x - 65, и вы получите 2147483520! Почему? Потому что промежуток между соседними float составляет в этом диапазоне 128, и операции с такими числами округляются до ближайшего числа с плавающей запятой.

В стандарте IEEE числа с плавающей запятой имеют фиксированную точность и записываются как числа по основанию 2 в научной нотации: 1.d1d2…dp1 х 2e, где p — это точность (24 для типа float, 53 для типа double). Интервал между двумя последовательными числами — величина 21-p+e, которая хорошо аппроксимируется величиной ε|x|, где ε — машинный эпсилон (21-p).

Зная интервал между соседними числами в окрестности некоторого числа с плавающей запятой, можно избежать классических ошибок в вычислениях. Например, при проведении итеративных вычислений, таких как поиск корня уравнения, нет смысла требовать большей точности, чем способна дать числовая система в окрестности решения. Следите, чтобы затребованная точность не оказалась меньше интервала между числами в окрестности решения, иначе вы войдете в бесконечный цикл.

Поскольку числа с плавающей запятой — лишь приближения действительных чисел, неизбежно наличие небольшой ошибки. Эта ошибка, называемая ошибкой округления, бывает причиной неожиданных результатов. Например, при вычитании очень близких чисел наиболее значимые цифры погашают друг друга, и тогда в результате положение наиболее значимых занимают те цифры, которые были наименее значимыми (и содержали в себе ошибку округления). В сущности, это непоправимо искажает все последующие вычисления. Такое явление называется размыванием (smearing). Нужно внимательно следить за своими алгоритмами, чтобы предотвратить это катастрофическое явление. Для иллюстрации возьмем решение уравнения x2 - 100000x + 1 = 0 по формуле корней квадратного уравнения. Поскольку операнды выражения -b + sqrt(b2 - 4) почти равны по величине, можно вместо этого вычислить корень r1 = -b - sqrt(b2 - 4), а затем получить r2 = 1/r1, так как в квадратном уравнении ax2 + bx + c = 0 корни удовлетворяют соотношению r1r2 = c/a.

Размывание может происходить и более скрытым образом. Допустим, что библиотека в коде наивно вычисляет ex по формуле 1 + x + x2/2 + x3/3! +…. Этот способ отлично работает для положительных x, но посмотрите, что произойдет, если x будет большим отрицательным числом. Члены с четными степенями окажутся большими положительными числами, а вычитание значений нечетных степеней почти не скажется на результате. Проблема в данном случае в том, что округление в больших положительных членах происходит в гораздо более значимых позициях, чем у правильного решения. Результат отклоняется к плюс бесконечности! Проблема решается просто: для отрицательных x используйте формулу ex = 1/e|x|.

Нечего и говорить, что числа с плавающей запятой нельзя использовать в финансовых приложениях — для них в таких языках, как Python и C#, есть классы десятичных чисел. Числа с плавающей запятой предназначены для эффективных научных расчетов. Но эффективность бесполезна, если теряется точность, поэтому помните, откуда идут ошибки округления, и пишите соответствующий код!

Загрузка...