Программирование на Java
Шрифт:
Math.IEEEremainder(9.0, 5.0) возвращает -1.0
Math.IEEEremainder(9.0, -5.0) возвращает -1.0
Math.IEEEremainder(-9.0, 5.0) возвращает 1.0
Math.IEEEremainder(-9.0, -5.0) возвращает 1.0
Унарные операторы инкрементации ++ и декрементации --, как обычно, можно использовать как справа, так и слева.
int x=1;
int y=++x;
В этом примере оператор ++ стоит перед переменной x, это означает, что сначала произойдет
int x=1;
int y=x++;
А в этом примере сначала значение x будет использовано для инициализации y, и лишь затем произойдет инкрементация. В результате значение x будет равно 2, а y будет равно 1.
Логические операторы
Логические операторы "и" и "или" ( & и | ) можно использовать в двух вариантах. Это связано с тем, что, как легко убедиться, для каждого оператора возможны случаи, когда значение первого операнда сразу определяет значение всего логического выражения. Если вторым операндом является значение некоторой функции, то появляется выбор – вызывать ее или нет, причем это решение может сказаться как на скорости, так и на функциональности программы.
Первый вариант операторов ( &, | ) всегда вычисляет оба операнда, второй же – ( &&, || ) не будет продолжать вычисления, если значение выражения уже очевидно. Например:
int x=1;
(x>0) | calculate(x) // в таком выражении
// произойдет вызов
// calculate
(x>0) || calculate(x) // а в этом - нет
Логический оператор отрицания "не" записывается как ! и, конечно, имеет только один вариант использования. Этот оператор меняет булевское значение на противоположное.
int x=1;
x>0 // выражение истинно
!(x>0) // выражение ложно
Оператор с условием ?: состоит из трех частей – условия и двух выражений. Сначала вычисляется условие (булевское выражение), а на основании результата значение всего оператора определяется первым выражением в случае получения истины и вторым – если условие ложно. Например, так можно вычислить модуль числа x:
x>0 ? x : -x
Битовые операции
Прежде чем переходить к битовым операциям, необходимо уточнить, каким именно образом целые числа представляются в двоичном виде. Конечно, для неотрицательных величин это практически очевидно:
0 0
1 1
2 10
3 11
4 100
5 101
и так далее. Однако как представляются отрицательные числа? Во-первых, вводят понятие знакового бита. Первый бит начинает отвечать за знак, а именно 0 означает положительное число, 1 – отрицательное. Но не следует думать, что остальные биты остаются неизменными. Например, если рассмотреть 8-битовое представление:
– 1 10000001 //
– 2 10000010 // это НЕВЕРНО!
– 3 10000011 // это НЕВЕРНО!
Такой подход неверен! В частности, мы получаем сразу два представления нуля – 00000000 и 100000000, что нерационально. Правильный алгоритм можно представить себе так. Чтобы получить значение -1, надо из 0 вычесть 1:
00000000
– 00000001
– -----------
– 11111111
Итак, -1 в двоичном виде представляется как 11111111. Продолжаем применять тот же алгоритм (вычитаем 1):
0 00000000
– 1 11111111
– 2 11111110
– 3 11111101
и так далее до значения 10000000, которое представляет собой наибольшее по модулю отрицательное число. Для 8-битового представления наибольшее положительное число 01111111 (=127), а наименьшее отрицательное 10000000 (=-128). Поскольку всего 8 бит определяет 28=256 значений, причем одно из них отводится для нуля, то становится ясно, почему наибольшие по модулю положительные и отрицательные значения различаются на единицу, а не совпадают.
Как известно, битовые операции "и", "или", "исключающее или" принимают два аргумента и выполняют логическое действие попарно над соответствующими битами аргументов. При этом используются те же обозначения, что и для логических операторов, но, конечно, только в первом (одиночном) варианте. Например, вычислим выражение 5&6:
00000101
& 00000110
– ------------
00000100 // число 5 в двоичном виде
// число 6 в двоичном виде
//проделали операцию "и" попарно над битами
// в каждой позиции
То есть выражение 5&6 равно 4.
Исключение составляет лишь оператор "не" или "NOT", который для побитовых операций записывается как (для логических было !). Этот оператор меняет каждый бит в числе на противоположный. Например, (-1)=0 . Можно легко установить общее правило для получения битового представления отрицательных чисел:
Если n – целое положительное число, то -n в битовом представлении равняется (n-1).
Наконец, осталось рассмотреть лишь операторы побитового сдвига. В Java есть один оператор сдвига влево и два варианта сдвига вправо. Такое различие связано с наличием знакового бита.
При сдвиге влево оператором << все биты числа смещаются на указанное количество позиций влево, причем освободившиеся справа позиции заполняются нулями. Эта операция аналогична умножению на 2n и действует вполне предсказуемо, как при положительных, так и при отрицательных аргументах.
Рассмотрим примеры применения операторов сдвига для значений типа int, т.е. 32-битных чисел. Пусть положительным аргументом будет число 20, а отрицательным -21.
// Сдвиг влево для положительного числа 20