Рубрики
Без рубрики

Расширение знака Java

Расширение знака – это операция увеличения битов при сохранении знака числа (положительного и отрицательного).

В Java Расширение знака сработает, если мы будем выполнять следующие действия:

  • Расширение примитивного преобразования – Приведение из одного типа в другой, которое включает увеличение битов исходного типа, например, приведение байт (8 бит) в int (32 бита) .
  • Побитовый сдвиг вправо на n бит >>n .

Расширение знака – это операция увеличения битов при сохранении знака числа (положительного и отрицательного). Расширение знака заполняет увеличенные биты оригиналом самого значащего бита (msb) или крайнего левого бита, за исключением одного условия – приведения беззнакового символа к другим типам, пожалуйста, прокрутите вниз до 3. Многоадресная рассылка Java ниже для объяснения.

В этой статье также объясняется знаменитая проблема многоадресной рассылки Java в приведенном ниже коде. Вы можете ответить на него?

  byte b = -1;                    // twos complement
  char c = (char) (b);            // byte -> int -> char (widening and narrowing type) (sign extension)
  System.out.println((int)c);     // char -> int (zero extension), output = 65535

  c = (char) (b & 0xff);          // byte -> int (sign extension)
  System.out.println((int) c);    // char -> int (zero extension), output = 255

1. Расширение Знака Java – Расширяющее Преобразование Примитивов

Например, мы приводим байт (8 бит) в int (32 бита).

Положительное число 1.1 Вот байт 10 в двоичном формате, 8 бит (1 байт).

0000 1010 = 10

Если мы приведем/расширим байт (8 бит) в int (32 бита, 4 байта), каковы значения (1 или 0) для дополнительных битов?

# byte, 1 byte, 8 bits
0000 1010 = 10

# int, 4 bytes, 32 bits
???? ???? | ???? ???? | ???? ???? | 0000 1010 = 10

Ответ – расширение знака; это зависит от “исходного” наиболее значимого бита (msb) или крайнего левого бита. В приведенном выше примере msb байт 10 или 0000 1010 является 0 , и расширение знака заполняет все дополнительные неизвестные биты нулем.

{0}000 1010 , {0} = most significant bit or leftmost bit.

После того, как мы приведем байт 10 к int , результат все равно будет 10 .

0000 0000 | 0000 0000 | 0000 0000 | 0000 1010 = 10
  byte aByte = 10;
  int aByte1 = (int) aByte;
  System.out.println(aByte1);  // output = 10

Отрицательное число 1.2 Для байта -10 , Java использует дополнение two для представления отрицательного значения.

# two's complement formula

0000 1010 = 10
1111 0101 (invert bits)
1111 0110 (add 1)
1111 0110 = -10

Если мы приведем/расширим байт -10 (8 бит) в int (32 бита, 4 байта) , каковы значения (1 или 0) для дополнительных битов?

???? ???? | ???? ???? | ???? ???? | 1111 0110 = -10

Для байта -10 или 1111 0110 , наиболее значительная часть 1111 0110 является 1 , и расширение Знака заполняет все неизвестные биты одним.

{1}111 0110 , {1} = most significant bit or leftmost bit.

Для байта -10 или 1111 0110 , наиболее значительная часть 1111 0110 является

1111 1111 | 1111 1111 | 1111 1111 | 1111 0110

Для ||байта -10||или || 1111 0110 ||, наиболее значительная часть || 1111 0110 || является || 1 || , и расширение Знака заполняет все неизвестные биты одним. После того, как мы приведем ||байт -10|| к ||int||, результат все равно будет || Для дополнения двух (крайний левый 1 отрицательный, 0 положительный)

# two's complement formula

1111 1111 | 1111 1111 | 1111 1111 | 1111 0110 (this is a negative number)
1111 1111 | 1111 1111 | 1111 1111 | 1111 0101 (sub 1)
0000 0000 | 0000 0000 | 0000 0000 | 0000 1010 (invert bits)
0000 0000 | 0000 0000 | 0000 0000 | 0000 1010 = 10
1111 1111 | 1111 1111 | 1111 1111 | 1111 0110 = -10
  byte aByte = -10;
  int aByte1 = (int) aByte;
  System.out.println(aByte1); // output = -10

Для байта -10 или

2. Для ||байта -10||или || 1111 0110 ||, наиболее значительная часть || 1111 0110 || является || 1 || , и расширение Знака заполняет все неизвестные биты одним. После того, как мы приведем ||байт -10|| к ||int||, результат все равно будет || Для дополнения двух (крайний левый 1 отрицательный, 0 положительный) Самое сложное – понять расширение знака ||tJava – Побитовый сдвиг вправо >> Дополнение wo ||.

Для байта -10 или

Для байта -10 или

  0000 0000 | 0000 0000 | 0000 0000 | 0000 1010

Для байта -10 или 1111 0110 , наиболее значительная часть 1111 0110 является

  ???0 0000 | 0000 0000 | 0000 0000 | 0000 0001 | 010 >> 3

Для байта -10 или 1111 0110 , наиболее значительная часть

  0000 0000 | 0000 0000 | 0000 0000 | 0000 0001 = 1
  System.out.println(10>>3); // output = 1

Дальнейшее чтение |/Java>> и >>> операторы побитового сдвига .

3. Расширение многоадресной рассылки Java – Знак и Нулевое расширение.

Пока все выглядит просто и прямолинейно. Настоящая проблема – это многоадресная рассылка, и это все усложняет.

Просмотрите следующий пример многоадресной рассылки Java (я нашел этот фрагмент кода с форума, если я не ошибаюсь, оригинал взят из книги головоломок Java).

  byte b = -1;                    // twos complement
  char c = (char) (b);            // byte -> int -> char (widening and narrowing type) (sign extension)
  System.out.println((int)c);     // char -> int (zero extension), output = 65535

  c = (char) (b & 0xff);          // byte -> int (sign extension)
  System.out.println((int) c);    // char -> int (zero extension), output = 255

Приведенный выше пример включал многоадресную рассылку из байта -> int -> char -> int , дополнение двух, расширяющее преобразование примитивов, сужающее преобразование примитивов, расширение знака, расширение нуля, а также побитовое маскирование. Давайте разберем это по порядку.

3.1 Java использует дополнение two для представления отрицательного.

  0000 0001 = 1
  1111 1110 = (invert bits)
  1111 1111 = (add 1)
  1111 1111 = -1 in twos complement

  byte b = 1111 1111

3.1 В Java байт является байтом со знаком, 8 бит; в то время как символ является беззнаковым 2 байта 16 бит. Java не может привести байт к креслу напрямую (невозможно привести подписанный тип к типу без знака), поэтому он приводит/|байт к int сначала и сужает до char .

  byte b = -1;  
  char c = (char) (b);                           = byte -> int -> char

  1111 1111                                      = byte (8 bits)
  1111 1111 | 1111 1111 | 1111 1111 | 1111 1111  = int  (32 bits, sign extension)
  1111 1111 | 1111 1111                          = char (16 bits)

  char c = 1111 1111 | 1111 1111

3.2 Этот (int)c интересен; он преобразует символ 2 байта в int 4 байта. Во-первых, мы можем подумать, что расширение знака будет работать и заполнит увеличенный бит исходным наиболее значимым битом, который равен единице.

  char c = 1111 1111 | 1111 1111
  int(c) = ???? ???? | ???? ???? | 1111 1111 | 1111 1111
  int(c) = 1111 1111 | 1111 1111 | 1111 1111 | 1111 1111 (sign extension) ??? but why the output is 65535?

Ответ – нет, в приведенном выше случае Java использует нулевое расширение для заполнения увеличенного бита НУЛЕМ.

Что такое Нулевое расширение? Нулевое расширение заполняет все увеличенные биты НУЛЕМ. Это нулевое расширение также применялось к логическому оператору сдвига вправо >>> .

Ниже приведен правильный ответ на int(c) .

char c = 1111 1111 | 1111 1111
int(c) = ???? ???? | ???? ???? | 1111 1111 | 1111 1111
int(c) = 0000 0000 | 0000 0000 | 1111 1111 | 1111 1111 (zero extension on unsigned char), the output is 65535.

3.4 Теперь мы рассмотрим следующее утверждение.

  c = (char) (b & 0xff);          // mask, int -> char
  System.out.println((int) c);    // char -> int, output: 255, but why?

Перепишите его на

  int i = (b & 0xff)
  c = (char)i
  System.out.println((int) c);

b & 0xff вернет значение int . Java приводит байт к int и выполняет побитовое & с этим 0xff , он же маскировка.

  byte b      = 1111 1111

  int         = 1111 1111 | 1111 1111 | 1111 1111 | 1111 1111  (sign extension)
                &
  0xff        = 0000 0000 | 0000 0000 | 0000 0000 | 1111 1111

  int i       = 0000 0000 | 0000 0000 | 0000 0000 | 1111 1111

  c = (char)i = 0000 0000 | 1111 1111                          (narrow down, int -> char)

Последний (int) c , приведите неподписанный символ к int , нулевое расширение.

  c           = 0000 0000 | 1111 1111

  (int)c      = ???? ???? | ???? ???? | 0000 0000 | 1111 1111  (zero extension)

  (int)c      = 0000 0000 | 0000 0000 | 0000 0000 | 1111 1111  

В дополнении два, первое крайнее слева обозначенное знаком, 1 – отрицательное число, 0 – положительное число.

  0000 0000 | 0000 0000 | 0000 0000 | 1111 1111
  = 1x2^0 +... 1x2^8
    1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 = 255

Сделано. Спасибо, что прочитали эту длинную статью.

Примечание Дайте мне знать, если вы нашли какие-либо опечатки, ошибки или неправильное объяснение.

Рекомендации

Оригинал: “https://mkyong.com/java/java-sign-extension/”