Главная » Java » Поразрядные операции

0

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

& поразрядное И

| поразрядное включающее ИЛИ

^ поразрядное исключающее ИЛИ (XOR)

Кроме того, имеется унарный оператор дополнения ~, который изменяет состояние каждого бита операнда на противоположное.  Дополнение целого значения 0x00003333 равняется 0xffffcccc.

Другие операторы, работающие с операндами на уровне битов, осуществляют сдвиг битов в целых значениях. Это следующие операторы:

<< сдвиг влево с заполнением правых битов нулями

>> сдвиг вправо с заполнением левых битов содержимым старшего (знакового) бита

>>>> > сдвиг вправо с заполнением левых битов нулями

В левой части оператора сдвига указывается сдвигаемое значение, а в правой — количество разрядов. Например, var>>>2 сдвигает биты переменной var на два разряда вправо и заполняет два старших бита нулями.

Правила типов для операторов сдвига несколько отличаются от тех, что применяются к прочим бинарным операторам. Тип результата оператора сдвига совпадает с типом левого операнда — то есть сдвигаемого значения. Если в левой части оператора сдвига стоит величина типа int, то результат сдвига также будет иметь тип int, даже если количество сдвигаемых разрядов было представлено в виде значения типа long.

Если количество разрядов сдвига превышает количество бит в слове или если оно окажется отрицательным, то фактическое количество разрядов сдвига будет отличаться от заданного. Оно будет представлять собой заданное количество с применением маски, равной размеру типа за вычетом единицы. Например, для 32-разрядного значения типа

int используется маска 0x1f (31), так что выражения n<<35 и n<<-29 будут эквивалентны

n<<3.

Поразрядные операторы могут также использоваться с логическими значениями. & и | возвращают те же значения, что и их логические аналоги && и ||, с одним важным отличием: в поразрядных операторах всегда вычисляются оба операнда, тогда как в логических операторах оба операнда вычисляются лишь в случае необходимости.

Поразрядный оператор ^ возвращает значение true, если операнды различаются — лишь один из них равен true, но не оба сразу. ^ предоставляет еще одну возможность для моделирования “логического исключающего ИЛИ”:

if ((x << 0) ^ (y << 0))

differentSign();

else sameSign();

Поразрядные операторы могут применяться только для значений целого и логического типа, но не для значений с плавающей точкой или ссылок. Операторы сдвига применяются только для целых типов. В тех редких случаях, когда вам понадобится манипулировать с битами в представлении величины с плавающей точкой, можно воспользоваться методами преобразования для классов Float и Double, рассмотренными  в разделе “Float и Double” на стр. .

5.19. Условный оператор

Условный оператор позволяет всего в одном выражении выбрать одно из двух значений на основании логической переменной. Приводимая ниже запись:

value = (userSetIt ? usersValue : defaultValue);

равнозначна следующей:

if (userSetIt)

value = usersValue;

else

value = defaultValue;

Главное отличие между операторами if и ?: заключается в том, что последний обладает собственным значением. Условный оператор обеспечивает более компактную запись, однако не все программисты соглашаются с тем, что он лучше воспринимается читателем программы. Мы пользуемся тем или иным вариантом в зависимости от обстоятельств. Использование скобок, окружающих выражения условного оператора, является вопросом личного вкуса, и на практике встречаются самые различные варианты. Сам язык не требует присутствия скобок.

Выражения-результаты (второе и третье) должны иметь тип, совместимый с операцией присваивания. Каждое из них должно быть таким, чтобы оно присваивалось другому без явного приведения типа. Тип результата условного оператора совпадает с более общим из типов двух возможных результатов. Например, в операторе

double scale = (halveit ? 1 : 0.5);

результаты относятся к типам int (1) и float (0.5). Значение типа int может быть присвоено переменной double, поэтому условный оператор также имеет тип double. Это правило сохраняется и для ссылок — если значение одного типа может присваиваться другому, то типом операции будет наиболее общий (наименее расширяемый) из них. Если ни один из этих типов не может быть присвоен другому, то операция является недопустимой.

Условный оператор иногда называют или оператором “вопросительный  знак/точка” из-за формы его записи, или “тернарным оператором”, поскольку это единственный тернарный (трех-операндный)  оператор в языке Java.

5.20. Операторы присваивания

Простой знак = является основной формой оператора присваивания. Java поддерживает много других разновидностей присваивания. Любой арифметический или бинарный поразрядный оператор может быть объединен с = для образования оператора присваивания. Например:

arr[where()] += 12;

приводит к тому же результату, что и

arr[where()] = arr[where()] + 12;

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

Для заданной переменной var типа Type, значения expr и бинарного оператора op запись

var op= expr

эквивалентна следующей:

var = (Type)((var) op (expr))

за исключением того, что значение var вычисляется всего один раз. Это означает, что запись op= допустима лишь в том случае, если оператор op может быть использован для типов, участвующих в выражении. Так, вы не сможете применить <<= с переменными типа double, потому что оператор сдвига << не работает с double.

Обратите внимание на использование скобок в приведенной выше записи. Выражение

a *= b + 1

эквивалентно

a = a * (b + 1)

но не

a = a * b + 1

Хотя a += 1 — то же самое, что и ++a, запись с использованием ++ счи- тается более наглядной, и потому ей отдается предпочтение.

5.21. Имена пакетов

Имя пакета представляет собой последовательность  идентификаторов,  разделяемых точками (.). В течение некоторого времени текстовые редакторы с поддержкой Unicode еще будут оставаться редкостью, так что в именах пакетов, предназначенных  для широкого распространения, стоит ограничиваться ASCII-символами.

Упражнение 5.2

На основании того, что вы узнали в этой главе (но без написания программ на Java!), определите, какие из приведенных ниже выражений являются неверными, а также укажите тип и значение верных выражений:

3 <<<< 2L -1 (3L <<<< 2) -1

10 << 12 == 6 >> 17

10 <<<< 12 == 6 >>>> 17

13.5e-1 % Float.POSITIVE_INFINITY Float.POSITIVE_INFINITY + Double.POSITIVE_INFINITY Double.POSITIVE_INFINITY + Float.POSITIVE_INFINITY

0.0 / -0.0 == -0.0 / 0.0

Integer.MAX_VALUE + Integer.MIN_VALUE Long.MAX_VALUE + 5;

(short)5 * (byte)10

(i << 15 ? 1.72e3f : 0)

i++ + i++ + –i    // исходное значение i = 3

Глава 6

ПОРЯДОК ВЫПОЛНЕНИЯ

— Скажите, пожалуйста, куда мне отсюда идти?

— Это во многом зависит от того, куда ты хочешь прийти.

Льюис Кэрролл, “Алиса в Стране Чудес”,

перевод Б. Заходера

Программа, состоящая из последовательно выполняемых операторов, несомненно способна принести пользу, так как операторы выполняются в том порядке, в котором они встречаются в программе. Однако возможность управлять порядком следования операторов (то есть проверять некоторое условие и в зависимости от результатов проверки выполнять разные действия) знаменует собой качественно новый уровень в средствах разработки. В этой главе рассмотрены практически все управляющие операторы, которые определяют порядок выполнения программы. Исключения рассмотрены отдельно в главе 7.

Источник: Арнольд К., Гослинг Д. – Язык программирования Java (1997)

По теме:

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