Как работает оператор сдвига битов в Java?

Я не изучал ИТ, и только совсем недавно наткнулся на сдвиги в битах и заявку на дополнение к двум. Итак, можете ли вы использовать простой английский в своих объяснениях и предположить, что я почти ничего не знаю об IP-адресах, битовых операциях и типах данных Java?

Сегодня я нашел следующий фрагмент кода (сокращенно):

long m = (-1) << (byte) 16;

Теперь это для маскировки IP-подсети. Я знаю, что мне нужно начать с 4 блоков по 8 бит (т.е. 4 байта), и все биты должны быть "включены": 11111111 11111111 1111111 1111111 Затем нули сдвигаются вправо, в этом случае значение составляет 16 бит; так мы получаем 11111111 11111111 00000000 0000000, маска.

Но у меня есть несколько вопросов:

  1. Ли 16 должны быть типа byte чтобы это работало?
  2. Результат имеет тип long, Когда выражение выше работает, -1 эффективно конвертируется в блоки 4x8bit. Откуда Java знает, что ей нужно 32 позиции / биты (длина IP-адреса), а не, скажем, 16 или 8 при применении дополнения до двух? (Я предполагаю, что это связано с long тип данных?)
  3. Почему два дополнения применяются к -1 начать с? (Google дает вам -0b1 если спросить что -1 в двоичном Сначала я подумал, что это может быть связано с переполнением, но это не так, правда...?)
  4. Действительно, в какие типы данных компилятор преобразует это во время выполнения кода, чтобы все это работало?

ОБНОВЛЕНИЕ: 16 производится во время выполнения методом; Я просто привел здесь константу в качестве примера. Оглядываясь назад, вероятно, плохая идея...

2 ответа

Действительно, в какие типы данных компилятор преобразует это во время выполнения кода, чтобы все это работало?

это

(-1) << (byte) 16;

это постоянное выражение. Его значение известно во время компиляции. Это long со значением -65536 (в десятичном представлении).

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

Возьмите например

int i = -1;
long m = i << (byte) 16;

Вышеупомянутое выражение включает оператор сдвига и два операнда, один из которых имеет тип int и другой тип byte,

JLS заявляет следующее относительно операторов сдвига и их операндов

Унарное числовое продвижение ( §5.6.1) выполняется для каждого операнда отдельно.

который

В противном случае, если операнд имеет тип byte, short или char типа времени компиляции, он повышается до значения типа int путем расширяющегося примитивного преобразования ( §5.1.2).

Итак byte значение увеличивается до int, Так что нет на ваш первый вопрос.

Результатом выражения будет значение типа int (32 бита). Это должно быть назначено long (64 бита), поэтому значение будет расширено доlong перед тем как быть назначенным.

Снова JLS

Целочисленными типами являются byte, short, int и long, значения которых составляют 8-разрядные, 16-разрядные, 32-разрядные и 64-разрядные целые числа с двумя дополнительными числами со знаком соответственно и char, значения которых являются 16-разрядными целыми числами без знака. представляющие кодовые единицы UTF-16 (§3.1).

Вот как они хранятся.

Это на самом деле сбивает с толку, что ваш m переменная имеет long тип, потому что IP-адрес является 32-разрядным и соответствует int, Ваша правая сторона действительно int и только после того, как он полностью вычислен, он распространяется на long (64-бит). Отвечая на ваши вопросы:

  1. Это не так. Вы можете удалить актерский состав.
  2. Результат на самом деле имеет тип int, но преобразуется в long потому что тип m требует этого.
  3. На самом деле дополнение к двум не "применяется" ни к чему. Число -1 закодирован в два дополнения. Вам нужен какой-то способ представления отрицательных чисел только с битами. Плюс, здесь дополнение двух играет второстепенную роль: это примерно -1 кодируется как все 1-бит.
  4. Это всего лишь блок из 32 однобитовых смещений влево, нули заполняют вакансию. Затем, чтобы преобразовать в long, Еще 32 1-битных добавлены на левой стороне.
Другие вопросы по тегам