Странное поведение, когда Java преобразует int в байт? - PullRequest
126 голосов
/ 09 мая 2009
int i =132;

byte b =(byte)i; System.out.println(b);

Mindboggling. Почему вывод -124?

Ответы [ 11 ]

165 голосов
/ 09 мая 2009

В Java int составляет 32 бита. A byte составляет 8 bits.

Большинство примитивных типов в Java подписаны, а byte, short, int и long кодируются в дополнение к двум. (Тип char является беззнаковым, и понятие знака неприменимо к boolean.)

В этой числовой схеме старший значащий бит указывает знак числа. Если требуется больше битов, самый старший бит («MSB») просто копируется в новый MSB.

Итак, если у вас есть байт 255: 11111111 и вы хотите представить его как int (32 бита), просто скопируйте 1 влево 24 раза.

Теперь, один из способов прочитать отрицательное число дополнения до двух - начать с наименее значимого бита, двигаться влево до тех пор, пока не найдете первую 1, а затем инвертировать каждый бит. Полученное число является положительной версией этого числа

Например: 11111111 переходит на 00000001 = -1. Это то, что Java будет отображать в качестве значения.

То, что вы, вероятно, хотите сделать, это знать беззнаковое значение байта.

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

Итак:

byte signedByte = -1;
int unsignedByte = signedByte & (0xff);

System.out.println("Signed: " + signedByte + " Unsigned: " + unsignedByte);

распечатал бы: "Signed: -1 Unsigned: 255"

Что на самом деле здесь происходит?

Мы используем побитовое И, чтобы замаскировать все посторонние знаковые биты (1 слева от младших 8 бит). Когда int преобразуется в байт, Java отсекает самые левые 24 бита

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

Поскольку 32-й бит теперь является знаковым битом, а не 8-м битом (и мы устанавливаем бит знака в 0, который является положительным), исходные 8 бит из байта считываются Java как положительное значение.

86 голосов
/ 31 января 2012

132 в цифрах ( база 10 ) составляет 1000_0100 в битах ( база 2 ), а Java хранит int в 32 битах:

0000_0000_0000_0000_0000_0000_1000_0100

Алгоритм для int-to-byte является усеченным слева; Алгоритм для System.out.println равен дополнения до двух (если дополняется до двух, если самый левый бит равен 1, интерпретировать как отрицательный дополнение к своему (инвертированные биты) минус один) Таким образом System.out.println(int-to-byte( )) это:

  • интерпретировать как (if-leftmost-bit-is-1 [отрицательный (инвертированные биты (минус один (] left-truncate (0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = интерпретировать как (if-leftmost-bit-is-1 [отрицательный (инвертированные биты (минус один (] 1000_0100 [)))])
  • = интерпретирует-как (отрицательный (инвертировать-бит (минус один (1000_0100))))
  • = интерпретирует-а (отрицательный (инвертный-бит (* 1 031 *))) * * тысяча тридцать две
  • = интерпретируют-а (отрицательный (* * один тысяча тридцать-четыре))
  • = интерпретируют-а (отрицательный (124))
  • = интерпретируют-а (-124) * +1039 *
  • = - Тада 124 !!!
23 голосов
/ 09 мая 2009

байт в Java подписан, поэтому он имеет диапазон от -2 ^ 7 до 2 ^ 7-1, то есть от -128 до 127. Поскольку 132 выше 127, вы в конечном итоге обернетесь вокруг 132-256 = -124. То есть по существу 256 (2 ^ 8) добавляются или вычитаются, пока не попадут в диапазон.

Для получения дополнительной информации вы можете прочитать о дополнении до двух .

16 голосов
/ 09 мая 2009

132 находится вне диапазона байта, который составляет от -128 до 127 (Byte.MIN_VALUE до Byte.MAX_VALUE) Вместо этого верхний бит 8-битного значения обрабатывается как подписанный, что указывает на его отрицательность в этом случае. Таким образом, число составляет 132 - 256 = -124.

3 голосов
/ 04 июля 2016

Уравнение к двум дополнениям:

enter image description here


В Java byte (N = 8) и int (N = 32) представлены дополнением 2s, показанным выше.

Из уравнения a 7 является отрицательным для byte, но положительным для int.

coef:   a7    a6  a5  a4  a3  a2  a1  a0
Binary: 1     0   0   0   0   1   0   0
----------------------------------------------
int:    128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 =  132
byte:  -128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 = -124
3 голосов
/ 13 апреля 2015

вот очень механический метод без отвлекающих теорий:

  1. Преобразовать число в двоичное представление (используйте калькулятор, хорошо?)
  2. Скопируйте только самые правые 8 бит (LSB) и отбросьте остальные.
  3. Из результата шага # 2, если самый левый бит равен 0, используйте калькулятор для преобразования числа в десятичное. Это ваш ответ.
  4. В противном случае (если крайний левый бит равен 1), ваш ответ отрицательный. Оставьте все самые правые нули и первый ненулевой бит без изменений. И поменял местами остальные, то есть заменил 1 на 0, а 0 на 1. Затем используйте калькулятор для преобразования в десятичное число и добавьте знак минуса, чтобы указать, что значение является отрицательным.

Этот более практичный метод соответствует многим теоретическим ответам выше. Так что те, кто все еще читает те книги по Java, в которых говорится, что они используют модуль, это определенно неправильно, так как 4 шага, которые я описал выше, определенно не являются операциями по модулю.

2 голосов
/ 23 июля 2016

Быстрый алгоритм, имитирующий способ его работы, следующий:

public int toByte(int number) {
    int tmp = number & 0xff
    return (tmp & 0x80) == 0 ? tmp : tmp - 256;
}

Как это работает? Посмотрите на daixtr ответ. Реализация точного алгоритма, описанного в его ответе, следующая:

public static int toByte(int number) {
    int tmp = number & 0xff;
    if ((tmp & 0x80) == 0x80) {
        int bit = 1;
        int mask = 0;
        for(;;) {
            mask |= bit;
            if ((tmp & bit) == 0) {
                bit <<=1;
                continue;
            }
            int left = tmp & (~mask);
            int right = tmp & mask;
            left = ~left;
            left &= (~mask);
            tmp = left | right;
            tmp = -(tmp & 0xff);
            break;
        }
    }
    return tmp;
}
2 голосов
/ 05 января 2014

часто в книгах вы найдете объяснение приведения от int к байту как выполняемого делением по модулю. это не совсем правильно, как показано ниже в действительности происходит сброс 24 старших значащих битов двоичного значения целого числа, что приводит к путанице, если установлен оставшийся крайний левый бит, который обозначает число как отрицательное

public class castingsample{

public static void main(String args[]){

    int i;
    byte y;
    i = 1024;
    for(i = 1024; i > 0; i-- ){

      y = (byte)i;
      System.out.print(i + " mod 128 = " + i%128 + " also ");
      System.out.println(i + " cast to byte " + " = " + y);

    }

}

}
0 голосов
/ 18 апреля 2019

Если вы хотите понять это математически, например, как это работает

, поэтому в основном числа от ч / б до -128–127 будут записываться так же, как и их десятичное значение, выше его (вашего числа - 256).

например. 132, ответ будет 132 - 256 = - 124 т.е.

256 + ваш ответ на номер 256 + (-124) - 132

Другой пример

double a = 295.04;
int b = 300;
byte c = (byte) a;
byte d = (byte) b; System.out.println(c + " " + d);

Выход будет 39 44

(295 - 256) (300 - 256)

ПРИМЕЧАНИЕ: числа после десятичной дроби не учитываются.

0 голосов
/ 13 июля 2017

Концептуально повторные вычитания 256 делаются на ваш номер, пока он не окажется в диапазоне от -128 до +127. Итак, в вашем случае вы начинаете с 132, а затем заканчиваете с -124 за один шаг.

В вычислительном отношении это соответствует извлечению 8 младших разрядов из вашего исходного числа. (И обратите внимание, что наиболее значимый бит из этих 8 становится знаковым битом.)

Обратите внимание, что в других языках это поведение не определено (например, C и C ++).

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