Странное поведение, когда Java преобразует int в байт?

130
int i =132;

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

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

Harshit
источник

Ответы:

172

В Java int- 32 бита. А byteравно 8 bits.

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

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

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

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

Например: 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 как положительное значение.

Wayne
источник
1
Молодец, лучшее объяснение по этому поводу, Уэйн! Я просто ищу математическую формализацию, почему в представлении с двумя дополнениями знаковый бит может быть скопирован справа, чтобы добавить биты. Это легко понять, если следовать правилу получения отрицательного числа. то есть: рассмотреть все биты справа налево и записать их без изменений, пока не будет составлена ​​первая 1. Затем инвертируйте последующие биты. Если я считаю, что недостающий бит равен 0, легко понять, что все они идут в 1. Но я искал более «математическое» объяснение.
AgostinoX
Что здесь signedByte & (0xff)происходит, так 0xffэто то, что это межчисленный литерал, таким образом, signedByte становится целым перед выполнением побитовой операции.
Кевин Уиллер
Это не 0xFF, в вашем примере это 0x7E!
JohnyTex
89

132в цифрах ( база 10 ) - 1000_0100в битах ( база 2 ), а в Java хранится int32 бита:

0000_0000_0000_0000_0000_0000_1000_0100

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

  • интерпретировать как (если-самый левый-бит-равен-1 [отрицательный (инвертировать-биты (минус-один (] левый-усечение ( 0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = интерпретировать как (если крайний левый бит равен 1 [отрицательный (инвертировать биты (минус один (] 1000_0100[)))])
  • = интерпретировать как (отрицательный (инвертировать-биты (минус-один ( 1000_0100))))
  • = интерпретировать как (отрицательный (инвертировать-бит ( 1000_0011)))
  • = интерпретировать как (отрицательный ( 0111_1100))
  • = Интерпретируют-а (отрицательный (124))
  • = Интерпретируют-а (-124)
  • = -124 Тада !!!
Pacerier
источник
7
Очень красиво объяснено
ZAJ
1
Итак, теперь 132 в десятичной системе счисления равно -124 в байтах. Как работает обратное?
Nilesh Deokar
@NileshDeokar, реверс от POLA, так как подходят (; ср JLS 5.1.2 ); вывод совпадает со знаком-левой панелью ( 0для положительного и 1отрицательного).
Pacerier
Что такое ПОЛА? Преобразование из intв a byte- это преобразование с потерями (т. Е. Информация теряется). Следовательно, нет возможности преобразовать его обратно в исходное intзначение.
truthadjustr
23

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

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

bdonlan
источник
16

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

Питер Лоури
источник
5

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

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

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

truthadjustr
источник
Какие книги по Java говорят об использовании «по модулю»? Я никогда не видел ни одной книги по CS, в которой говорилось бы об этом за 46 лет, не говоря уже о книге по Java. Что по модулю? В Java нет операции по модулю. Только оператор остатка.
Marquis of Lorne
grep сложнее. http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdfстраница 59
truthadjustr
4

Уравнение с дополнением до двух:

введите описание изображения здесь


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

Из уравнения 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
bcorso
источник
2

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

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);

    }

}

}
ручка
источник
2
Я никогда не видел этого ни в одной книге за 46 лет.
Marquis of Lorne
2

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

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;
}
Франсиско Нето
источник
1

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

поэтому в основном числа ч / б от -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

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

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

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

Вирсавия
источник
Чтобы было ясно, результат, который вы получите, такой же, как если бы выполнялись повторные вычитания. На практике JVM этого не делает. (Это было бы ужасно неэффективно!)
Stephen C
На самом деле. Надеюсь, мой второй абзац описывает, как JVM на самом деле это делает. Но я немного повозился со своим языком.
Вирсавия
1
Да. Изменение «по существу» на «концептуально» имеет огромное значение!
Stephen C
-1
 N is input number
case 1: 0<=N<=127  answer=N;
case 2: 128<=N<=256 answer=N-256 
case 3: N>256   
        temp1=N/256;
        temp2=N-temp*256;
        if temp2<=127   then answer=temp2;
        else if temp2>=128  then answer=temp2-256;
case 4: negative  number input
        do same procedure.just change the sign of the solution           
devil_29
источник
Правильный ответ достигается битовой маской, а не делением на остаток.
Marquis of Lorne