Что произойдет? Это пример переполнения, когда в результате вычислений получается слишком большое число, чтобы его можно было правильно представить. Поведение системы в таких случаях обычно не определено, но в рассматриваемой ситуации переменной toobig присваивается специальное значение, которое обозначает бесконечность, и функция printf() отображает либо inf, либо infinity (или какую- то другую вариацию на эту тему).
А что можно сказать о делении очень малых чисел? Здесь ситуация более сложная. Вспомните, что число типа float хранится в виде сочетания показателя степени и значащей части, или мантиссы. В рассматриваемом случае это число, имеющее минимально возможный показатель степени, а также наименьшее значение, которое использует все доступные биты, отведенные для представления мантиссы. Это будет наименьшее число, представленное с наибольшей точностью, доступной для типа float. Теперь разделим его на 2. Обычно это приводит к уменьшению показателя степени, но в данном случае показатель уже достиг своего нижнего предела. Происходит сдвиг битов мантиссы вправо, с освобождением первой позиции и потерей последней двоичной цифры. Аналогичная картина возникает, если выбрать 10 в качестве основания системы счисления, взять число с четырьмя значащими цифрами, например,
0.1234Е-10, и разделить его на 10, получив в итоге 0.0123Е-10. Вы получите результат,
104 Глава 3 но в процессе деления потеряете цифру. Эта ситуация называется потерей значимости, а значения с плавающей запятой, которые утратили полную точность типа, в языке С называются субнормальными. Таким образом, деление наименьшего положительного значения с плавающей запятой на 2 дает субнормальное значение. Деление на достаточно большое значение приведет к потере всех цифр, и вы получите в результате 0. В настоящее время библиотека С предоставляет функции, которые позволяют проверить, не приведут ли вычисления к субнормальным значениям.
Существует еще одно специальное значение с плавающей запятой: NaN (not-a-num- ber — не число). Например, вы передаете функции asin() некоторое значение, а она возвращает угол, для которого переданное значение является синусом. Однако значение синуса не может быть больше 1, поэтому функция не определена для значений, превышающих 1. В таких случаях функция возвращает значение NaN, которое функция printf() отображает в виде nan, NaN или каким-то похожим образом.
Ошибки округления данных с плавающей запятой
Возьмите некоторое число, прибавьте к нему 1 и затем отнимите от суммы исходное число.
Что вы получите в результате? Вы получите 1. Тем не менее, вычисления с плавающей запятой вроде показанного ниже могут дать другой результат:

Вывод выглядит следующим образом:
0.000000 к- старая версия компилятора gcc в операционной системе Linux
-13584010575872.000000 +-Turbo С 1.5
4008175468544.000000 +-XCode 4.5, Visual Studio 2012,
текущая версия компилятора gcc
Причина получения таких странных результатов состоит в том, что компьютер не следит за тем, чтобы под числа с плавающей запятой было отведено столько десятичных позиций, сколько нужно для правильного выполнения операции. Число 2.0е20 представлено цифрой 2, за которой следует 20 нулей, и за счет прибавления 1 вы пытаетесь изменить 21-ю цифру. Чтобы эта операция выполнилась корректно, программа должна иметь возможность хранить число, состоящее из 21 цифры. Число типа float — это обычно шесть или семь цифр, масштабированных при помощи показателя степени до большего или меньшего числа, так что такая попытка сложения обречена на неудачу. С другой стороны, если вместо 2.0е20 вы укажете 2.0е4, то получите правильный ответ, поскольку вы пытаетесь изменить пятую цифру, а числа типа float обладают достаточной для этой операции точностью.
Представление значений с плавающей запятой
В предыдущей врезке было видно, что вывод одной и той же программы отличался в зависимости от используемой компьютерной системы. Причина этого отличия в том, что существует много разных способов реализации представления чисел с плавающей запятой в рамках описанных общих подходов. Для обеспечения большего единообразия в Институте инженеров по электротехнике и радиоэлектронике (IEEE) разработан стандарт для представления чисел с плавающей запятой и вычислений с плавающей запятой, который теперь применяется во многих аппаратных блоках обработки чисел с плавающей запятой.
Читать дальше