Операторы

From AsIsWiki
Revision as of 10:02, 4 April 2015 by Alex (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Форум

Назад | Оглавление | Дальше


Contents

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

В Java используются обычные арифметические операторы: + - * / %

15 / 2 = 7	    // целочисленное деление

15.0 / 2 = 7.5	    // деление чисел с плавающей точкой

15 % 2 = 1	    // выделение остатка от деления

15 / 0 = exception  // генерация исключения

15.0 / 0 = NaN	    // результат - бесконечность

Сокращенная запись бинарных (двух-операндовых) арифметических операторов:

x += 4;  // x = x + 4;

x *= 4;  // x = x * 4;

x /= 4;  // x = x / 4;

x -= 4;  // x = x - 4;

x %= 4;  // x = x % 4;

С точки зрения кросс-платформенных систем, вещественная арифметика в Java имеет некоторые проблемы. Тип double оперирует 64-битовыми числами, но некоторые процессоры используют 80-разрядные регистры с плавающей точкой. Эти регистры обеспечивают дополнительную точность на промежуточных этапах вычислений. Например:

double w = x * y / z;

Многие Intel-процессоры вычисляют x * y и сохраняют промежуточный результат в 80-битном регистре, затем делят результат на z и в конце округляют ответ до 64 бит. Так можно повысить точность вычислений, избежав переполнения.

Но результат может измениться, если использовать 64-разрядный процессор. По этой причине, в первоначальном описании виртуальной машины Java указывалось, что все промежуточные результаты должны округляться. Однако, округление может привести к переполнению, и вычисления при этом происходят медленнее.

Чтобы разрешить конфликт между производительностью и воспроизводимостью результатов, разработчики придумали следующее:

По умолчанию в виртуальной машине в промежуточных вычислениях может использоваться повышенная точность. Однако методы, помеченные ключевым словом strictfp, должны использовать вещественную арифметику, гарантирующую воспроизводимость результатов. Например, метод main() можно записать так:

public static strictfp void main(String[] args)

В этом случае, все команды внутри метода main() будут выполнять точные операции над числами с плавающей точкой.

По умолчанию, промежуточный результат может использовать расширенный показатель степени, но не расширенную мантиссу (Intel-чипы поддерживают округление мантиссы без потери производительности). Т.е. единственное различие вычислений по умолчанию и strictfp состоит в возможности переполнения.


Инкремент и декремент

int n = 12;

n++;  // увеличиваем n на 1

n--;  // уменьшаем n на 1

Операторы ++ и -- нельзя использовать с числами, например, выражение 4++ является недопустимым.

Существует постфиксная и префиксная форма операторов ++ и --:

int m = 7;

int n = 7;

int a = 2 * ++m;  // a = 16, m = 8, префиксная форма

int b = 2 * n++;  // b = 14, n = 8, постфиксная форма


Операторы отношения и логические операторы

Операторы отношения:

3 == 7  // false

3 != 7  // true

3 > 7   // false

3 < 7   // true

3 <= 7  // true

3 => 7  // false

Логические операторы:

1 && 0 = 0  // логическое И

1 || 0 = 1  // логическое ИЛИ

   ! 1 = 0  // логическое НЕ

Операторы && и || работают по сокращенной схеме: если первый элемент определяет значение всего выражения, то остальные элементы не вычисляются. В приведенном ниже выражении вторая часть не вычисляется, если x == 0:

x != 0 && 1 / x > x + y  // не делить на 0

Аналогично, (выражение1) || (выражение2) == true, если выражение1 == true, и вычислять выражение2 нет необходимости.

Тернарный оператор "?":

условие ? выражение1 : выражение2

Если условие истинно, то вычисляется выражение1, иначе вычисляется выражение2.

(x < y ? x : y)  // возвращается меньшее из чисел x и y


Побитовые операции

Побитовые операторы:

&  И
|  ИЛИ
^  Исключающее ИЛИ
~  НЕ

Пример определения состояния 4-го бита целого числа n:

int fourthBitFromRight = (n & 8) / 8;

Используя оператор & можно выделить любую цепочку бит числа, путем маскировки остальных бит нулями.

С логическими переменными операторы & и | дают логические значения.

В отличие от && и ||, в сложных выражениях с операторами & и | выполняются все действия.

Операторы >> и << сдвигают битовое представление числа вправо или влево.

Сдвиг удобно использовать при построении битового представления на основе маски:

int fourthBitFromRight = (n & (1 << 3)) >> 3;

Оператор >>> заполняет старшие разряды нулями, в то время как оператор >> восстанавливает в старших разрядах знаковый бит. Оператора <<< в Java нет.

При попытке сдвига на значение, превышающее количество бит числа, происходит сокращение значения сдвига, по модулю 32 или 64 (в зависимости от типа данных). Например:

1 << 35 эквивалентно 1 << 3


Математические функции и константы

Класс Math содержит набор необходимых математических функций.

double x = 4;

double y = Math.sqrt(x);

System.out.println(y);  // выводит число 2.0

Метод println() принадлежит объекту System.out (где, out - объект, определенный в классе System и представляющий стандартное устройство вывода).

Метод sqrt() принадлежит классу Math, а не объекту. Такие методы называются статическими.

Кроме того, класс Math содержит:

double y;

y = Math.pow(x, a);  // x ^ a

y = Math.sin(x);

y = Math.cos(x);

y = Math.tan(x);

y = Math.atan(x);

y = Math.atan2(x);

y = Math.exp(x);

y = Math.log(x);

y = Math.PI;

y = Math.E;

При вызове математических функций класс Math можно не указывать, включив вместо этого в начало исходника следующее выражение:

import static java.lang.Math.*;

Для повышения производительности, функции класса Math используют подпрограммы аппаратного модуля, предназначенного для операций с вещественными числами.

Если скорость не очень важна, но необходимо получить повторяемость результатов на различных платформах, следует использовать класс StrictMath. Этот класс реализует алгоритмы из библиотеки fdlibm, гарантирующей повторяемость результатов. Исходники алгоритмов: http://www.netlib.org/fdlibm/index.html.

В библиотеке fdlibm каждая функция определена неоднократно, поэтому в классе StrictMath в соответствии IEEE 754 имена функций начинаются с "e".


Преобразование числовых типов

                     char

                      v
                      v
		      
byte  >>  short  >>  int   >>  long   >  float

		      v         v
		      
                     float >>  double << int

>> - преобразования, которые выполняются без потери информации.

> - преобразования, при которых может произойти потеря точности, например: число 123456789, преобразованное в тип float, имеет тот же порядок, но меньшую точность:

int n;

float f = n;  // f = 1.234567892E8

При бинарных операциях типы преобразуются по следующим правилам:

  • если один из операндов имеет тип double, то второй тоже преобразуется в double
  • иначе, если один из операндов имеет тип float, то второй тоже преобразуется в float
  • иначе, если один из операндов имеет тип long, то второй тоже преобразуется в long
  • иначе, если один из операндов имеет тип int, то второй тоже преобразуется в int


Приведение числовых типов

Приведение типов (cast) задается парой скобок, внутри которых указывается желаемый тип, а затем имя переменной:

double x = 9.997;

int nx = (int) x;  // nx = 9, дробная часть отбрасывается

Округление вещественных чисел повышает точность приведения:

double x = 9.997;

int nx = (int) Math.round(x);  // nx = 10

При вызове метода round() по-прежнему нужно выполнять приведение (int), поскольку возвращаемое им значение имеет тип long.

Если результат приведения типов выходит за пределы допустимого диапазона, то происходит усечение:

byte n = (byte) 300;  // n = 44

Приведение логических значений к целым и наоборот невозможно.

Для представления логического значения в виде целого, можно использовать тернарный оператор:

boolean b = true;

int n = b ? 1 : 0;


Иерархия операторов

Операторы Ассоциативность
[]  .  () (вызов метода) Слева направо
!  ~  ++  --  +(унарный)  -(унарный)  ()(приведение)  new Справа налево
*  /  % Слева направо
+  - Слева направо
<<  >>  >>> Слева направо
<  <=  >  >=  instanceof Слева направо
==  != Слева направо
& Слева направо
^ Слева направо
| Слева направо
&& Слева направо
|| Слева направо
?: Справа налево
=  +=  -=  *=  /=  %=  |=  ^=  <<=  >>=  >>>= Справа налево

Если скобки не используются, сначала выполняются более приоритетные операции.

Операторы, находящиеся на одном уровне иерархии, выполняются слева направо, за исключением операторов, имеющих правую ассоциативность. Например: поскольку оператор && приоритетнее ||, выражение a && b || c эквивалентно (a && b) || c.

Так как оператор += ассоциируется справа на лево, выражение a += b += c означает a += (b += c).


Нумерованные типы

Предположим, что вы продаете пиццу четырех размеров: малого, среднего, большого, и очень большого. Можно представить размер целыми числами, например 1, 2, 3, 4, или буквами S, M, L, X. Однако, в процессе составления программы можно присвоить переменной недопустимое значение, например 0 или m. В подобных ситуациях лучше использовать нумерованный тип:

enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE};

Size s = Size.MEDIUM;

Переменная типа Size может содержать только предопределенные значения. Допустимо также значение null, указывающее на то, что данной переменной не присвоена никакая величина.



Форум

Назад | Оглавление | Дальше

Personal tools
Namespaces

Variants
Actions
Navigation
Tools