Главная » Java » Арифметика с плавающей запятой

0

В процессе выполнения арифметических операций над аргументами с плавающей запятой возможны переполнение разрядной сетки до бесконечности (результат превышает верхнюю границу диапазона изменения значений типа float или doublе) и потеря значимости – нередко до нуля7 (результат слишком мал для типа float или doublе). В результате вычисления некорректных выражений (таких как, например, деление бесконечности на бесконечность) получается значение NaN – "не число".

Арифметические операции над конечными операндами с плавающей запятой, удовлетворяющими диапазонам точности представления значений float и doublе, выполняются вполне предсказуемо. Правило присваивания знака результату также традиционно: при умножении и делении чисел с одним знаком результат положителен, а если знаки различны – отрицателен.

Сложение двух бесконечностей дает в результате такую же бесконечность, если их знаки одинаковы, и NaN – если знаки различны. При вычитании бесконечностей одного знака будет получено значение NaN; вычитание бесконечностей с различными знаками дает в итоге бесконечность с тем знаком, который имеется у левого операнда. Например, (? – (-?)) есть ?. Результат вычисления арифметического выражения, одним из операндов которого является NaN, всегда равен NaN. Переполнение дает в итоге бесконечность соответствующего знака, а потеря значимости – значение (возможно, нулевое) соответствующего знака. При выполнении арифметических операций с плавающей запятой поддерживается отрицательный нуль, -0.0, который в контексте операторов сравнения равен положительному нулю, +0.0. Хотя оба нуля считаются равноценными, в конкретных выражениях они способны приводить к различным результатам. Так, например, результат вычисления выражения 1f/0f равен положительной бесконечности, а выражения 1f/-0f – отрицательной бесконечности.

Если итогом исчезновения значащих разрядов является -0.0 и если -0.0 == 0.0, каким образом можно выявить факт получения отрицательного нуля? Следует поместить тестируемое нулевое значение в выражение, где знак способен себя проявить, и проверить результат. Пусть, например, х содержит нулевое значение; тогда Выражение l/х будет равно отрицательной бесконечности, если х – отрицательный нуль, и положительной бесконечности – в противном случае.

Правила выполнения операций с бесконечными величинами совпадают с теми, которые приняты в математике. Алгебраическое сложение любой конечной величины с бесконечностью дает в результате ту же бесконечность. Например, (-? + х) равно -? при любом конечном значении х.

Бесконечные значения в Jаvа-программе задаются с помощью символических констант POSITIVE_INFINITY (положительная бесконечность) и

 

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

 

NEGATIVE_INFINITY (отрицательная бесконечность), объявленных в классах-оболочках float и double. Например, Double.NEGATIVE_INFINITY указывает на версию значения отрицательной бесконечности, определенную в классе Double.

 

Умножение бесконечности на нуль дает в результате NaN. При умножении бесконечности на ненулевое конечное значение будет получена бесконечность соответствующего знака.

В остальных ситуациях оператор вычисления остатка (%) от деления аргументов с плавающей запятой действует аналогично случаю, когда он применяется к целочисленным аргументам. В Java поддерживается и иной способ вычисления остатка _ информация о соответствующем методе IEEEremainder приведена в

разделе 17.6 на странице 491.

: strictfp и не-strictfp

Операции над числовыми аргументами с плавающей запятой могут выполняться в двух режимах – точном (в языке Java он обозначается служебным словом strictfp) и неточном. Вычисления, осуществляемые в точном режиме, следуют строгим правилам, которые гарантируют получение совершенно равноценных результатов в среде всех виртуальных машин Java. Арифметические операции над значениями с плавающей запятой в неточном режиме выполняются в соответствии с несколько ослабленными правилами. Такие правила позволяют виртуальной машине использовать иные модели представления чисел с Плавающей запятой, иногда помогающие избежать эффектов переполнения разрядной сетки или потери значимости, которые могли бы возникнуть в точном режиме. На практике это означает, что некоторые приложения, предусматривающие использование неточного режима, способны вести себя по-разному под Управлением различных виртуальных машин. Неточные вычисления могут выполняться несколько быстрее по сравнению с точными, поскольку ослабленные Правила дают виртуальной машине возможность выбора альтернативной модели Представления чисел, которая поддерживается непосредственно на уровне соответствующего аппаратного обеспечения.

Признаком точного режима выполнения операций с плавающей запятой служит наличие модификатора strictfp в объявлении класса, интерфейса или отдельного метода. Если в объявлении метода содержится модификатор strictfp, это значит, что все инструкции метода, предусматривающие обработку чисел с плавающей запятой, должны действовать в точном режиме. Если же признаком strictfp помечены объявления класса или интерфейса, все соответствующие выражения в теле класса, включая и те, которые расположены в блоках инициализации и объявлениях вложенных типов, будут выполняться точно. Для определения того, должно ли выражение вычисляться в точном режиме, следует проверить объявления соответствующих метода, класса или интерфейса. Если хотя бы в одном из них присутствует модификатор strictfp, выражение будет вычислено точно. Например, код внутреннего класса, объявленного в составе внешнего класса или метода, которые обозначены как strictfp, будет выполняться точно. Необходимо подчеркнуть, что признак точного режима вычислений не наследуется – наличие в объявлении базового класса или интерфейса модификатора strictfp отнюдь не означает, что таким же свойством будут обладать и про из водные типы.

Постоянные выражения, операндами которых служат числа с плавающей запятой, всегда вычисляются точно. КОД, который не снабжен признаком strictfp, может быть выполнен в соответствии с алгоритмами, не предусматривающими получения заведомо воспроизводимых результатов. Если необходимо гарантировать абсолютное, вплоть до отдельных битов, совпадение решений на всех без исключения виртуальных машинах Java, надлежит явно обозначить модификатором strictfp объявления подходящих методов, классов и интерфейсов. Следует заметить, что виртуальная машина в состоянии обеспечить получение совершенно точных решений даже тогда, когда она действует согласно правилам "неточного" режима: эти правила не заставляют машину работать неточно – они всего лишь предлагают ей некоторую степень свободы для оптимизации кода в тех случаях, когда условия конкретной задачи не содержат требований абсолютного совпадения результатов.

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

Если вам необходима более полная информация по рассмотренной теме, обращайтесь к официальному документу The Java™ Laпguage Specificatioп.

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

  • Комментарии