2009-05-09 2 views

ответ

149

В Java, int - 32 бит. A byte - 8 bits.

Все в Java подписано, и byte s, int s, long s закодированы в дополнении к двум.

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

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

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

Например: 11111111 is to 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 как положительный стоимость.

+5

Собственные символы не подписаны. – Rollerball

+0

молодец, лучшее объяснение на эту тему, Уэйн! Я просто ищу математическую формализацию, почему в представлении о дополнении двух битов знака можно скопировать справа, чтобы добавить бит. Легко понять, что он думает о том, как получить отрицательное число. то есть: рассмотрите все биты справа налево и напишите их без изменений до первого первого. Затем инвертируйте последующие биты. Если я считаю, что недостающий бит равен 0 с, легко понять, что все они относятся к 1. Но я искал более «математическое» объяснение. – AgostinoX

+0

Whats hapenning здесь 'signedByte & (0xff)' - это то, что '0xff' является литералом интергера, поэтому signedByte получает продвижение до целого числа до выполнения побитовой операции. –

22

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

Для получения дополнительной информации, вы можете прочитать на two's complement.

14

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

70

Чтобы понять, как это работает, нам нужно знать, что компьютеры работают в битах.

132 в base 10 (десятичный) 10000100 в base 2 (двоичный). Поскольку Java хранит int в 32 бит, то, что мы имеем

00000000_00000000_00000000_10000100

Когда int превращается в byte, Java отбивные-офф самый левый 24 бит. Мы получаем 10000100.

В two's complement самый левый бит используется как знак. Если самый левый бит равен 0, ничего дальше делать не будет.

Если самый левый бит равен 1 (как здесь, здесь), это означает, что число отрицательное и требуется больше работы. Для того, чтобы получить величину, мы минус один затем применить one's complement (применить свое дополнение означает, что мы инвертировать биты):

  1. 10000100 - 1 = 10000011

  2. Интертировать 10000011 = 01111100

01111100 при понимании десятичного числа, составляет 124.

Итак, у нас есть отрицательное число с величиной 124, что дает нам -124.

+2

Очень красиво объяснено – ZAJ

2

Часто в книгах вы найдете объяснение отливки из int в байт как выполняемое модульным разделением. это не совсем правильно, как показано ниже , что происходит на самом деле является 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); 

    } 

} 

} 
+0

Я никогда не видел этого в любой книге за 46 лет. – EJP

1

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

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

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

+0

* Что * Java-книги говорят использовать 'modulo'? Я никогда не видел ни одной книги из CS, в которой говорится, что через 46 лет, не говоря уже о любой Java-книге. Что такое «модулю»? В Java нет модульной операции. Только оператор остатка. – EJP

1

Two's complement Equation:

enter image description here


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

Из уравнения, А отрицательно для 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 
1

Быстрый алгоритм, который имитирует способ, которым это работа заключается в следующем:

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; 
} 
+0

Вы не можете вернуться из 'toByte()', если он объявлен функцией 'void' ... –

+0

Исправлено, спасибо @JohnPerry –

-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   
+0

Правильный ответ получен бит-маской, а не делением и остатком. – EJP

0

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

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

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

+0

Чтобы быть ясным, результат, который вы получаете, тот же *, как если бы * были сделаны повторные вычитания. На практике JVM на самом деле не делает этого таким образом. (Это было бы ужасно неэффективно!) –

+0

Действительно. Надеюсь, что мой второй параграф описывает, как JVM на самом деле это делает. Но я немного поиграл с моим языком. – Bathsheba

+1

Да. Изменение «по существу» на «концептуально» имеет огромное значение! –

Смежные вопросы