Неявное заполнение двоичного литерала слева в Java - PullRequest
1 голос
/ 05 августа 2020

Когда я построил маску для получения наиболее значимого бита в формате дополнения 2, я обнаружил неожиданное поведение.

Чтобы проверить, активен ли самый старший бит в 8-битном знаке число, я мог бы получить бит следующим образом.

byte value = -1;
long byteSignMask = 0b1000_0000;
value & byteSignMask;

Результат идентичен независимо от того, что я использую 0b1000_0000 или 1L << 7 для byteSignMask. На самом деле проходит следующий код.

long byteSign1 = 1L << 7;
long byteSign2 = 0b1000_0000;
// OK
assertEquals(byteSign1, byteSign2);

Но я сделал это для типа int; аналогично, результат ожидался.

long intSign1 = 1L << 31;
long intSign2 = 0b1000_0000_0000_0000_0000_0000_0000_0000;

// Fail: expected:<2147483648> but was:<-2147483648>
assertEquals(intSign1, intSign2);

На самом деле, они разные.

// intSign1 = 10000000000000000000000000000000
System.out.println("intSign1 = " + Long.toBinaryString(intSign1));
// intSign2 = 1111111111111111111111111111111110000000000000000000000000000000
System.out.println("intSign2 = " + Long.toBinaryString(intSign2));

Похоже, буквальная маска целого числа (intSign1) дополнена слева 1, в то время как операция сдвига не вызывает такого эффекта.

Почему целое число, выраженное двоичным литералом, автоматически дополняется слева единицей? Есть ли официальная документация, описывающая такое поведение?

1 Ответ

2 голосов
/ 05 августа 2020

intSign2 здесь:

0b1000_0000_0000_0000_0000_0000_0000_0000

Это int литерал, не a long literal.

Итак, вы говорите: «Я хочу, чтобы значение int было представлено этим битовым шаблоном».

Один 1, за которым следуют 31 0 s, представленные как 32-битное целое число со знаком с дополнением до двух, int, это -2147483648. Это значение затем "расширяется" до long, когда вы назначаете переменную типа long intSign2. Вот откуда взялись заполненные единицы.

Чтобы сделать его long буквальным, вам нужно будет добавить суффикс L:

0b1000_0000_0000_0000_0000_0000_0000_0000L

Почему byteSign2 дополняется левым нулем, а intSign2 дополняется левой единицей? тип данных, он всегда будет дополняться нулями слева. Итак, в случае byteSign2 вы сказали 0b1000_0000, что фактически эквивалентно этому двоичному литералу:

0b0000_0000_0000_0000_0000_0000_1000_0000

В случае intSign2 вы указали полные 32 бита int, , поэтому заполнение не выполняется вообще.

Единицы с левым заполнением являются результатом преобразования int -to- long, которое имело место. Согласно спецификации языка , это преобразование работает следующим образом:

Расширяющее преобразование целочисленного значения со знаком в целочисленный тип T просто sign-extends представление целочисленного значения с дополнением до двух для заполнения более широкого формата.

Поскольку преобразование «знак-расширяется», оно добавит 1 с, если бит знака равен 1, и 0s, если бит знака равен 0 (это сохраняет знак числа, отрицательные числа остаются отрицательными, et c). Для вашего двоичного литерала бит знака равен 1, поэтому он дополняет 1 с.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...