Дивна поведінка, коли Java перетворює int в байт?


Відповіді:


172

У Java int- 32 біт. А 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 біт.) Коли інт перетворюється в байт, Java відсікає найбільш ліві 24 біти

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

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


1
молодець, найкраще пояснення з цього приводу, Уейн! Я просто шукаю формалізацію математики, чому в представленні доповнення двох можна скопіювати біт знаків справа, щоб додати біти. Це легко зрозуміти, думаючи про правило, як отримати від’ємник числа. тобто: розгляньте всі біти справа наліво і запишіть їх незмінними до першого складу. Потім переверніть наступні біти. Якщо я вважаю, що пропущений біт дорівнює 0, легко зрозуміти, що всі вони переходять до 1. Але я шукав більш "математичного" пояснення.
AgostinoX

Що тут signedByte & (0xff)відбувається, це те, що 0xffє міжрядковим буквеним символом, таким чином підписанийByte переходить до цілого числа до того, як буде виконуватися побітова операція.
Кевін Вілер

Це не 0xFF, це 0x7E у вашому прикладі!
JohnyTex

89

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( )):

  • interpret-as (if-leftmost-bit-is-1 [negative (invert-bits (minus-one (] left-усікання ( 0000_0000_0000_0000_0000_0000_1000_0100) [))]])
  • = interpret-as (if-leftmost-bit-is-1 [negative (invert-bits (minus-one (] 1000_0100[))]])
  • = інтерпретувати як (мінус (інверт-біти (мінус-один ( 1000_0100))))
  • = інтерпретувати як (негативне (інверт-біти ( 1000_0011)))
  • = інтерпретувати як (мінус ( 0111_1100))
  • = інтерпретувати як (негативне (124))
  • = інтерпретувати як (-124)
  • = -124 Тада !!!

7
Дуже красиво пояснено
ZAJ

1
Отже, 132 у десятковій частині становить -124 в байтах. Як працює реверс?
Нілеш Деокар

@NileshDeokar, Зворотній бік - POLA, оскільки вони відповідають (; пор. JLS 5.1.2 ); вихід збігається із знаком ліворуч ( 0для позитивного та 1для негативного).
Pacerier

Що таке POLA? Перетворення з intв a byte- це конверсія втрати (тобто інформація втрачається). Тому немає можливості перетворити його назад у початкове intзначення.
truthadjustr

23

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

Для отримання додаткової інформації ви можете прочитати доповнення двох .


16

132 знаходиться поза діапазоном байтів, який становить від -128 до 127 (байт.MIN_VALUE до байта.MAX_VALUE) Натомість верхній біт 8-бітного значення трактується як підписаний, що вказує на негативне значення в цьому випадку. Отже число - 132 - 256 = -124.


5

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

  1. Перетворіть число у двійкове подання (використовуйте калькулятор, добре?)
  2. Скопіюйте лише найправіші 8 біт (LSB), а решту відкиньте.
  3. З результату кроку №2, якщо крайній лівий біт дорівнює 0, використовуйте калькулятор для перетворення числа в десяткове. Це ваша відповідь.
  4. В іншому випадку (якщо крайній лівий біт дорівнює 1), ваша відповідь негативна. Залиште всі кращі нулі та перший ненульовий біт без змін. І реверсували решту, тобто замінюйте 1 на 0 і 0 на 1. Потім використовуйте калькулятор для перетворення в десятковий і додайте від'ємний знак, щоб вказати, що значення є негативним.

Цей більш практичний метод відповідає багатьом теоретичним відповідям вище. Отже, ті, хто все ще читає ті книги Java, які говорять про використання модуля, це, безумовно, неправильно, оскільки чотири кроки, які я описав вище, напевно не є модульною операцією.


Які книги Java говорять про використання модуля? Я ніколи не бачив жодної книги CS, яка б говорила про те, що за 46 років, не кажучи вже про будь-яку книгу на Java. Що таке "модуль"? На Java не відбувається модульної операції. Тільки оператор, що залишився.
Маркіз Лорн

грепнути важче. http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdfсторінка 59
truthadjustr

4

Рівняння доповнення двох:

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


У Java byte(N = 8) і int(N = 32) представлені 2-доповненням, показаним вище.

З рівняння, 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

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

    }

}

}

2
Я жодного разу не бачив цього в жодній книзі за 46 років.
Маркіз Лорн

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

Якщо ви хочете зрозуміти це математично, наприклад, як це працює

тому в основному числа b / w -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 насправді не робить це таким чином. (Це було б жахливо неефективно!)
Стівен C

Справді. Я сподіваюся, що мій другий параграф стосується того, як СВМ насправді робить це. Але я трохи поспілкувався зі своєю мовою.
Вірсавія

1
Так. Зміна "по суті" на "концептуально" має величезну зміну!
Стівен С

-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           

Правильна відповідь дістається шляхом бітового маскування, а не поділом та залишком.
Маркіз Лорн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.