Думаю, все программисты хоть раз, прямо или косвенно, сталкивались с проблемой неточных вычислений, когда вроде числа имеют не так уж много цифр после запятой, операции не слишком сложные, но в результате всё равно получается бред.
Или же наоборот, числа в вычислениях настолько большие, что компьютер просто не в состоянии их обработать.
В этой статье разберёмся, почему так происходит и можно ли это исправить.
Слишком большие числа
На картинке сверху, где мы берём корень из числа, которое однозначно не является квадратом результата, явно видно округление. Поскольку количество девяток после запятой в результате больше, чем вмещает в себя стек типа float, то все остальные девятки просто округлились.
Поэтому производить какие-либо операции с большими числами нужно с особой осторожностью. Что ж, теперь перейдём к более сложным вычислениям, в мир дробей и систем счисления.
Числа с плавающей точкой
Если провести небольшой эксперимент и попробовать произвести некоторые арифметические операции с числами, у которых разная величина части после запятой, то можно заметить, что некоторые из получившихся выражений будут посчитаны неверно, независимо от того, как много у чисел знаков после запятой.
Проблема тут кроется не в величине чисел, а в совсем другом. Всем известно, что процессор умеет вычислять только в двоичной системе счисления, десятичную он на самом деле не понимает, а только имитирует это понимание. Давайте попробуем перевести в двоичную систему число 0.21.
Получится что-то вроде 0.001101011100001010001111010111000010..... Число, по всей видимости, бесконечное, а количество информации в одной переменной ограничено, поэтому оно обрезается, пусть будет 0.0011010111000010. Теперь то же самое проделаем и с числом 0.1: 0.0001100110011001. Вычислим разность результатов: 0.0001110000101001. И переводим в 10-тичную с.с.: 0.1100006103515625. Получилась ужасная погрешность, хотя и не в ту сторону, как в python, возможно там 2-ичные числа имеют больше знаков после запятой. В любом случае, здесь погрешность довольно велика, а значит она останется и в результате округления, так и получается это 0.10999999999999999.
А, например, при вычислении выражения 0.201 - 0.1 погрешность маленькая, округление просто уничтожает её, поэтому результат верный.
Что же делать?
Очевидно, что для решения проблемы с большими числами нужно просто увеличить размер переменных и количество знаков для округления. Но со второй проблемой не всё так однозначно.
Как вы уже поняли, виноваты не процессоры и не языки программирования, для решения этой проблемы нужно менять само представление чисел двоичной системы счисления, а это неоправданно и глупо. Вообще, для решения этой проблемы стоит задуматься, а требует ли она решения в принципе. Ведь всегда можно выделить больше памяти для переменных и тем самым сократить погрешность до минимума, говорят, что для того, чтобы вычислить длину окружности размером с нашу галактику с погрешностью менее, чем в миллиметр, достаточно числа Пи со всего лишь 50-тью знаками после запятой.
Обобщая, проблема, на мой взгляд, не острая и достойна упоминания только в юмористических и образовательных целях. Тем не менее, тема интересная, надеюсь и вам она понравилась.
А на сегодня всё, пока!