Логические операции и действительные переменные...58

We use cookies. Read the Privacy and Cookie Policy

Переменные с плавающей точкой, как уже отмечалось, не могут использоваться для перечисления. Вы можете сказать: первый, второй, третий, четвёртый и т.д., так как соотношения между 1, 2, 3 абсолютно точно известны. Но нет никакого смысла говорить о номере 4.535887 в последовательности ( такой способ нумерации возможен лишь как обозначение чего-то между четвёртым и пятым, но не действительного значения номера, так как в любом сколь угодно малом отрезке их несчётное[ 9 ] множество ).

Тип float, представляющий в С++ действительные числа, не является перечислимым. Кроме того, в отличие от действительных чисел, числа с плавающей точкой имеют ограниченное количество разрядов, поэтому при использовании операторов сравнения с числами с плавающей точкой необходимо соблюдать осторожность. Рассмотрим следующий пример:

    float f1 = 10.0 ;

    float f2 = f1 / 3 ;

    f1 == ( f2 * 3.0 ) ; /* Равны ли эти значения? */

__________________

9Более того, в данном случае это не красивое слово, а строгий математический термин. — Прим. ред.

_________________

58 стр. Часть 1. Первое знакомство с С++

Сравнивая начальное и полученное значения, мы не обязательно получим равенство. Действительные переменные, с которыми работает компьютер, не могут содержать бесконечное число значимых разрядов. Поэтому f2 равняется, например, 3.3333, а не 31/3. В отличие от математики, в компьютере число троек после точки ограничено. Умножив 3.3333 на 3, вы, вероятно, получите не 10.0, а 9.9999. Такой маленькой разницей может пренебречь человек, но не компьютер. Эта машина понимает под равенством исключительно точное равенство значений.

«Переменная типа float поддерживает точность около 6 значащих цифр, а double — 13. Я говорю "около", так как компьютер часто генерирует числа наподобие 3.3333347 из-за особенностей вычислений с плавающей точкой.»

[Технические подробности]

В "чистой" математике количество троек после десятичной точки бесконечно, но компьютер не в состоянии работать с бесконечными числами. Поэтому при умножении 3.3333 на 3 мы получим 9.9999, а не 10, которое должно получаться при умножении 31/3 на 3 — так называемая ошибка округления. Такие малые отличия двух чисел несущественны для человека, но не для компьютера. Равенство означает в точности точное равенство ( неплохой каламбур? ).

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

Проблемы могут появиться и при совершенно простых вычислениях, например:

      float f1 = 10.0 ;

      float f2 = 100 % 30 ;

      f1 == f2 ; /* истинно ли это выражение? */

Теоретически f1 и f2 должны быть равны ( об операции деления по модулю можно прочитать в главе 3, "Выполнение математических операций" ). Ошибка округления возникнуть вроде бы не должна. Однако и в этом нельзя быть уверенным: вам ведь неизвестно, как именно представляются числа с плавающей точкой внутри компьютера. Позвольте порекомендовать более безопасное сравнение:

      float f1 = 10.0 ;

      float f2 = f1 / 3 ;

      float f3 = f2 * 3.0 ;

      ( f1 - f3 ) < 0.0001 && ( f3 - f1 ) < 0.0001 ;

Оно истинно в том случае, если разница между f1 и f2 меньше какого-то малого значения ( в нашем случае — 0.0001 ); при этом небольшие погрешности вычислений на правильность сравнения не повлияют.