Неожиданные эффекты при работе с числами с плавающей запятой

Рубрика:  Арифметика с плавающей запятойIEEE-754Основы

Показаны три основные проблемы, возникающие при работе с числами с плавающей запятой.

Описанный способ представления чисел даёт ряд преимуществ перед числами с фиксированной запятой и удобен для работы с весьма широким диапазоном значений, однако он порождает и некоторые недостатки в виде неожиданных эффектов, непривычных для математика-программиста.

Первый недостаток состоит в том, что далеко не каждое действительное число из допустимого диапазона найдёт свою позицию на нашей числовой оси. Вернёмся к нашему примеру, в котором $p=3$, $e_{min}=-1$ и $e_{max}=2$. Эти ограничения позволяют охватить диапазон чисел [0,5; 7] вместе с числом 0, которое мы договорись выделять отдельно. Как теперь отразить число 3,75? Оно находится в точности между двумя допустимыми числами: 3,5 и 4:

В подобных случаях число округляется по одному из четырёх правил, о которых мы поговорим позже, в другой статье. Пока примите как должное то, что по умолчанию оно будет округлено до значения 4. То есть 3,75≈4. Получается, что весь континуум чисел каким-то образом должен отразиться на наших всего лишь семнадцати значениях. Следовательно, это порождает определённую погрешность в вычислениях и делает неотличимыми друг от друга числа, близкие к какому-то из точно представимых значений. Так, например, все действительные числа из диапазона [3,75; 4,5] будут по умолчанию округлены до 4. Более того, весь наш интервал можно разбить на такие зоны, числа в каждой из которых будут округлены к одному и тому же значению (к той единственной риске, которая попадает в зону одного цвета):

Ещё раз напомню, что существует 4 режима округления, о которых мы поговорим позже. Рисунок приведён для одного из этих режимов, который используется по умолчанию. Обратите, пожалуйста, внимание на то, что длинные риски находятся не в центре своих зон. Это связано с тем, что с увеличением экспоненты на один вдвое растягиваются интервалы между числами.

Второй недостаток связан с тем, что некоторые арифметические операции могут давать результат, не представимый имеющимся набором чисел. С одной стороны, это очевидно, ведь сумма или произведение двух самых больших чисел является ещё большим числом, которое уже не уместится в выбранный диапазон. Однако менее очевиден другой эффект: попробуйте сложить 4+0,5. Вы получите снова 4, так как число 4,5 будет округлено до 4. Таким образом, попытка складывать и вычитать числа, очень сильно друг от друга отличающиеся, не имеет смысла, результат будет равен наибольшему по модулю числу из тех, что принимают участие в вычислениях.

Этот недостаток порождает довольно любопытный эффект, связанный с порядком вычислений. Давайте попробуем посчитать выражение 4+0,5−4. Мы получим 0, так как первым действием мы получаем 4+0,5≈4, а затем уже вычитаем 4−4=0. Однако если поменять порядок 4−4+0,5, то получим 0,5. Таким образом, плавающая арифметика не обладает свойством коммутативности для тех операций в математике, которые коммутативными являются. Аналогично, нарушается и ассоциативность: (4+0,5)+0,5=4 ≠ 5=4+(0,5+0,5). По указанным причинам нужно очень хорошо продумывать порядок вычислений, который позволит минимизировать погрешность, связанную с подобными эффектами.

Третий недостаток связан с вычитанием разных чисел, находящихся близко друг к другу. Возьмём два числа 1 и 0,875 (это число, которое предшествует единице на нашей числовой оси). Их разность равна 0,125. Однако проблема в том, что число 0,125 находится в так называемой «яме» между нулём и минимальным положительным числом 0,5. Таким образом, оно будет округлено до нуля:

То же самое произойдёт, если попытаться рассчитать разность 1,75−1,5=0{,}25≈0, точно так же получим 0. Что мы имеем? — Разность двух различных чисел может дать ноль! Это крайне неудобно и может приводить к серьёзным ошибкам в вычислениях, скажем, к непредвиденному делению на ноль или невероятной потере точности в промежуточных вычислениях сложных выражений.

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

Денормализованные числа

Нормализованные числа — это числа, которые можно представить в научной нотации вида 1,xxx…×2e, однако ограничения, наложенные на число знаков мантиссы и величину экспоненты всегда будут порождать «яму» между нулём и первым положительным числом, представимым в указанном виде. Таким образом, подобная запись исключает возможность работы с очень маленькими числами, близкими к нулю. Для решения этой проблемы мы должны отказаться от условия нормализации 1≤|m|<2 для тех чисел, экспонента которых имеет минимальное значение $e=e_{min}$. Так мы приходим к определению денормализованных чисел.

Денормализованными называются числа с минимальной экспонентой, для которых |m|<1, то есть они имеют вид $0{,}xxx\ldots\times 2^{e_{min}}$.

Дополним наш пример такими числами:

  • 0,00×2−1=0,
  • 0,01×2−1=0,125,
  • 0,10×2−1=0,25,
  • 0,11×2−1=0,375.

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

Денормализованные числа равномерно заполняют «яму» между нулём и минимальным нормализованным числом:

Обратите внимание, что шаг, на котором денормализованные числа отстоят друг от друга, равен шагу, на котором друг от друга отстоят нормализованные числа с той же экспонентой −1. В нашем примере этот шаг равен 0,125. Это позволяет полностью избавиться от третьей проблемы. Теперь гарантируется, что разность любых точно представимых чисел не будет равна нулю, если только эти числа не равны между собой. Вернёмся к нашим примерам расчёта разности: 1−0,875=0,125=0,01×2−1, а 1,75−1,5=0,25=0,10×2−1. Денормализованные числа также можно вычитать друг из друга, причём указанное правило не нарушится.

Если внимательно посмотреть на последний рисунок, то нетрудно увидеть, что всего чисел с отрицательной экспонентой (их 8) столько же, сколько чисел с положительной экспонентой (тоже 8), а числа, порядок которых равен нулю (их 4), находятся как будто в середине между ними. Такая симметрия достигается во всех случаях, когда мы выбираем $e_{min}=1-e_{max}$. Существует предположение, что именно это соображение легло в основу выбора диапазона экспонент для формата IEEE-754. Позже мы увидим, что в этом формате диапазон экспонент подчиняется указанной формуле.