17 апр. 2015 г.

Примитивные вещественные типы Java - float, double

  Тип   Содержит   По умолчанию   Размер   Диапазон   Обертки
   float    вещественное знаковое   0.0    32 bits    от 1.4E−45 до 3.4028235E+38    Float
   double    вещественное знаковое   0.0    64 bits    от 4.9E−324 до 1.7976931348623157E+308    Double

F0000101

Вещественные числа в Java представлены типами данных float и double. Как показано в таблицах выше, float является 32 битным значением с плавающей точкой, с обычной точностью, а double представляет 64 битное значение с плавающей точкой, с двойной точностью. Количество бит отведенные под представление этих чисел смотрите в таблице выше. Оба типа соответствуют стандарту IEEE 754-1985, который определяет формат чисел и арифметические операции, применимые к этим числам. Но есть и небольшие отличия от этого стандарта. К обычным вещественным числам добавляются еще четыре значения:

  • положительная бесконечность, выражаемая константой POSITIVE_INFINITY и возникающая при переполнении положительного значения, например в результате операции умножения 3.0*6e307 или при делении на нуль;
  • отрицательная бесконечность NEGATIVE_INFINITY, возникающая при переполнении отрицательного значения, например в результате операции умножения -3.0*6e307 или при делении на нуль отрицательного числа;
  • "не число", записываемое константой NaN (Not a Number) и возникающее, например, при умножении нуля на бесконечность.
  • кроме того, стандарт различает положительный и отрицательный нуль, возникающий при делении на бесконечность соответствующего знака, хотя сравнение 0.0 == -0.0 дает в результате истину, true.

Операции с бесконечностями выполняются по обычным математическим правилам. Во всем остальном вещественные типы — это обычные вещественные значения, к которым применимы все арифметические операции и операции сравнения.

Вещественные литералы

F00002

Значения с плавающей точкой можно непосредственно включать в Java программу. В такой величине за необязательной последовательностью цифр следует десятичная точка и другая последовательность цифр. Ниже представлены несколько примеров:

123.45
0.0
.01
5.

Литералы с плавающей точкой можно также представить в экспоненциальной, или научной, нотации, в которой за числом следует буква e или E (показатель степени) и другое число. Второе число представляет степень десятки, на которую умножается первое число. Если же число записано в шестнадцатеричном формате, то экспонента это степень двойки. Например:

1.2345E02 // 1.2345 × 102, или 123.45
1e-6  // 1 × 10-6, или 0.000001
6.02e23  // Число Авогадро: 6.02 × 1023

Так же с Java 6, возможно записывать в шестнадцатеричном формате:

0xFp2 // 15x22=60

Литералы с плавающей точкой по умолчанию являются значениями типа double. При включении значения типа float в программу за числом следует поставить символ f или F:

double d = 6.02E23;
float f = 6.02e23f;

В принципе литералы типа double можно тоже обозначать суффиксом d или D, но это особо не имеет смысла, так как вещественные литералы всегда по умолчанию double.

Большинство вещественных чисел, по самой их природе, нельзя точно представить каким-либо конечным количеством битов. Таким образом, необходимо помнить, что значения float и double являются только приближенными значениями представляемых ими чисел. float – это 32 битное приближение, которое дает как минимум 6 значимых десятичных разрядов, а double – это 64 битное приближение, которое представляет по крайней мере 15 значимых десятичных разрядов. На практике эти числа подходят для большинства вычислений с вещественными числами.

Поскольку типы с плавающей точкой в Java могут сводить переполнение к бесконечности, антипереполнение – к нулю, а также имеют особое NaN значение, то арифметические операции с плавающей точкой никогда не генерируют исключений, даже при выполнении недопустимых операций, например при делении нуля на нуль либо при вычислении корня отрицательного числа.

Бесконечные значения с плавающей точкой ведут себя вполне логично. Например, прибавление к бесконечности или вычитание из нее любого конечного значения дает бесконечность. Поведение отрицательного нуля почти не отличается от положитель ного нуля; фактически оператор равенства == сообщает о равенстве отрицательного и положительного нуля. Единственный способ отличить отрицательный нуль от по ложительного или обычного нуля – разделить на него какоелибо число. 1.0/0.0 дает положительную бесконечность, а деление 1.0 на отрицательный нуль дает отрицательную бесконечность. И наконец, поскольку NaN не является числом, оператор == сообщает, что это значение не равно ни одному другому числу, включая само значение! Чтобы проверить, являются ли значения float и double нечисловыми (NaN), следует вызвать методы Float.isNaN()  и Double.isNaN().

Арифметические операции

F00003

Поскольку к вещественным типам применимы все арифметические операции и сравнения, целые и вещественные значения можно смешивать в операциях. При этом правило приведения типов дополняется такими условиями:

если в операции один операнд имеет тип double, то и другой приводится к типу double;

иначе, если один операнд имеет тип float, то и другой приводится к типу float;

в противном случае действует правило приведения целых значений.

Побитовые операции с вещественными типами не поддерживаются.

Операция деление по модулю (или остаток) определяется так же как и для целочисленных типов:
a % b = a — (a / b) * b 

Так же для операции деления по модулю справедливо следующее выражение:

a = ((long)(a/b))*b+(a%b)

F00004

F00005

Вычисления чисел с плавающей точкой на одном и том же, а тем более на разных процессорах могут давать несколько разные результаты, поскольку виртуальная машина java выполняет эти операции на сопроцессоре (FPU), если он присутствует на устройстве. А в сопроцессоре обычно регистры для работы с  плавающей точкой 80 бит, что шире даже чем double. Поэтому, если в эти регистры положить числа double, а потом опять вернуть их в double, то результаты могут быть разные для одинаковых чисел, по умолчанию java эту проблему не решает, то есть нет гарантии, что написанный код работающий с целочисленными типами будет давать одинаковый результат на разных процессорах. Это сделано потому, что желательно использовать сопроцессор для скорости вычислений. Результаты в этом случае могут немного различаться.

Чтобы результаты были на всех процессорах одинаковые, то следует использовать ключевое слово strictfp в декларации метода, например:

public static strictfp void main(String[] args)

В данном случае все что будет происходить в методе main, будет происходить без участия сопроцессора, будет строго округляться в пределах 64 бит и результат будет одинаковым на разных процессорах.

Все математические функции из библиотеки  java.lang.Math работают с числами типа double.

Ну и теперь немного практики:

F00006

И вывод этой программы:

F00007

1 комментарий: