Компонентный подход в программировании

       

Типы чисел с плавающей точкой


Представление типов значений с плавающей точкой, float и double, а также операции с ними, соответствуют стандарту на вычисления с плавающей точкой IEEE 754 (он же — IEC 60559) [11,12]. Согласно этому стандарту, значение такого типа состоит из знакового бита, мантиссы и экспоненты (у значения float 23 бита отводятся на мантиссу и 8, на экспоненту, у double — 52 бита на мантиссу и 11 — на экспоненту).

Помимо обычных чисел, значения обоих типов включают -0.0 (кстати, написав так, вы получите обычный 0.0, поскольку этот текст будет воспринят как константа 0.0, к которой применен унарный оператор -; единственный способ получить -0.0 — конвертировать его битовое представление — в шестнадцатеричном виде для типа float он представляется как 0x80000000, а для double — 0x8000000000000000), положительные и отрицательные бесконечности (для типа float это 0x7f800000 и 0xff800000, а для double — 0x7ff0000000000000 и 0xfff0000000000000), а также специальное значение NaN (Not-A-Number, не число; оно может быть представлено любыми значениями, у которых экспонента максимальна, а мантисса не равна 0).

Для значений с плавающей точкой определены следующие операции.

  • ==, != — сравнения на равенство и неравенство. В соответствии с IEEE 754 NaN не равно ни одному числу, в том числе самому себе. -0.0 считается равным 0.0.
  • <, <=, >, >= — сравнения на основе порядка. +? больше, чем любой обычное число и -?, а -? меньше любого конечного числа. NaN несравнимо ни с одним числом, даже с самим собой — это значит, что любая указанная операция возвращает false, если один из ее операндов — NaN. -0.0 считается равным, а не меньше, чем 0.0.
  • +, -, *, /, % — сложение, вычитание, умножение, деление, взятие остатка по модулю, а также соответствующие операции присваивания с одновременным выполнением одного из этих действий. Все эти операции действуют согласно IEEE 754, кроме операции вычисления остатка, которая реализована так, чтобы при всех конечных a и b (b != 0) выполнялось a%b == a – b*n, где n — самое большое по абсолютной величине целое число, не превосходящее |a/b|, знак которого совпадает со знаком a/b.
    По абсолютной величине a% b всегда меньше b, знак a%b совпадает со знаком a.

    Согласно стандарту IEEE 754, все арифметические операции определены для бесконечных аргументов "естественным" образом: 1.0/0.0 дает +? -1.0/0.0 дает -?, 0.0/0.0 — NaN, конечное x в сумме с +? дает +?, а +?+(-?) — NaN. Если один из операндов NaN, то результат операции тоже NaN.

  • ++, -- — увеличение и уменьшение на единицу. Для бесконечностей и NaN результат применения этих операторов совпадает с операндом.


В Java в классах java.lang.Float и java.lang.Double есть константы, равные максимальному конечному значению типа, минимальному положительному значению типа, положительной и отрицательной бесконечностям и NaN.

Float.MAX_VALUE = (2-2-23)•2127

Float.MIN_VALUE = 2-149

Double.MAX_VALUE = (2-2-59)•21023

Double.MIN_VALUE = 2-1074

Бесконечности и NaN в обоих случаях называются POSITIVE_INFINITY, NEGATIVE_INFINITY и NaN.


В C# соответствующие классы System.Single и System.Double также хранят эти значения в виде констант MaxValue, Epsilon, PositiveInfinity, NegativeInfinity и NaN.


В C# есть еще один тип для представления чисел с плавающей точкой — decimal (тип-обертка для него называется System.Decimal).

Значения этого типа представляются 128 битами, из которых один используется для знака, 96 — для двоичной мантиссы, еще 5 — для представления десятичной экспоненты, лежащей от 0 до 28. Остальные биты не используются.

Представляемое знаком s (+1 или -1), мантиссой m (0–(296-1)) и экспонентой e (0–28) значение равно (-1)s•m•10-e.

Таким образом, значения этого типа могут, в отличие от стандартных типов float и double, представлять десятичные дроби с 28 точными знаками и используются для финансовых вычислений. Такая точность необходима, поскольку в этих вычислениях ошибки округления в сотые доли процента при накоплении за счет больших сумм и большого количества транзакций в течение нескольких лет могут привести к значительным суммам убытков для разных сторон.

Для типа decimal определены все те же операции, что и для типов с плавающей точкой, однако при выходе их результатов за рамки значений типа создается исключительная ситуация. Этот тип не имеет специальных значений -0.0, NaN и бесконечностей.


В Java классы, методы и инициализаторы могут быть помечены модификатором strictfp. Он означает, что при вычислениях с плавающей точкой в рамках этих деклараций все промежуточные результаты должны быть представлены в рамках того типа, к которому они относятся, согласно стандарту IEEE 754.

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

Содержание раздела