Как работает оператор смены битов Java под капотом?

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

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

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

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

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

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

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

2 ответа

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

Это

(-1) << (byte) 16;

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

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

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

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

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

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

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

который

В противном случае, если операнд имеет тип байта байта, короткий или char, ему присваивается значение типа int расширительным примитивом преобразование (§5.1.2).

Таким образом, значение byte расширяется до int. Так что нет вашего первого вопроса.

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

Из JLS снова

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

То, как они хранятся.


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

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

licensed under cc by-sa 3.0 with attribution.