int к byte или short Java просто отбрасывает старшие биты и оставляет младшие 8 или 16 (JLS, раздел 5.1.3). Если старший из оставшихся битов равен 1, число читается как отрицательное в дополнительном коде: поэтому (byte) 450 дает -62, а (short) 10_000_000 дает -27008. Ниже разбор по битам: как получить эти результаты вручную.Кратко
(byte) iи(short) iэто сужающее преобразование: остаются только младшие 8 или 16 бит, остальные отбрасываются без округления и без ошибки.- 450 в двоичной записи это
1 1100 0010; после отбрасывания лишних битов остается1100 0010. - Старший бит 1 означает отрицательное число в дополнительном коде, поэтому читать биты как обычное число нельзя.
- Чтобы получить модуль числа: вычесть единицу, инвертировать все биты (получится прямой код), перевести в десятичную систему и приписать минус:
1100 0010превращается в0011 1110= 62, ответ -62. - Обратный путь, от числа к дополнительному коду: инвертировать биты положительного представления и прибавить 1: для 15 получится
1111 0001, это -15. - Для
shortвсе то же самое, только остаются 16 бит:(short) 10_000_000дает -27008.

Пример 1: что дает (byte) 450
Почему, если привести какие-нибудь значения типаint к типу short или byte, результаты бывают неожиданными? Давайте разбираться!
int i = 450;
byte b = (byte)i;
System.out.println(b);
Результат на экране:
-62
Переводим 450 в двоичную систему
Неожиданно, но этому есть логическое объяснение, более того, это действие можно сделать собственноручно. Для этого, нам нужно перевести 450 из десятичной системы в двоичную:
450/2. 0
225/2. 1
112/2. 0
56/2. 0
28/2. 0
14/2. 0
7/2. 1
3/2. 1
1/2. 1
В правый столбик от числа мы записываем его остаток от деления на 2, а под самим числом пишем результат деления нашего числа на два, если остаток 0. Если остаток 1, то ниже записываем целую часть от деления на два. (Онлайн калькулятор с объяснением вычислений).
В итоге, мы получаем, что в двоичной системе счисления 450 = 1 1100 0010.
Любое число типа int занимает 4 байта или 32 бита, где каждый бит - это 0 или 1. В нашем случае заняты только 9 битов, и в принципе наш int i = 450 в двоичной системе выглядит так:
0000_0000_0000_0000_0000_0001_1100_0010
Мы хотим записать нашу переменную в переменную типа byte, но число типа byte занимает 1 байт (следует из названия этого типа) или 8 бит. Поэтому лишние биты слева просто отбрасываются, и в итоге мы получаем:
1100 0010
Знаковый бит и дополнительный код
Диапазон значений типаbyte: от -128 до 127. Каждое число занимает 8 бит и у каждого числа крайний левый бит - знаковый бит (sign bit). У всех положительных чисел он равен 0, у всех отрицательных он равен 1.
Спешить переводить наш результат, полученный выше, в 10-ную систему не нужно, т.к. мы получили дополнительный код искомого числа, а не прямой. Крайний бит слева получился равен 1, следовательно число у нас отрицательное, а у отрицательных чисел прямой и обратный код не совпадает, в отличие от положительных. Если бы знаковый бит был равен 0, то мы могли бы сразу перевести число в десятичную систему счисления и получить: 66.
Но знаковый бит равен 1, поэтому сначала дополнительный код нужно перевести в прямой и добавить к ответу знак минус.Тренировка: дополнительный код числа -15
Для наглядности и тренировки, сначала попробуем получить дополнительный код какого-нибудь числа, например -15. Для этого в прямом коде его положительного представления (числа 15) нужно поменять все 0 на 1 и наоборот (получить обратный код, также называют инверсный), а затем добавить к результату единицу. В двоичной системе 15 =0000 1111;
Обратный код (меняем все 0 на 1 и наоборот) = 1111 0000;
Дополнительный код (прибавляем единицу):
1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 1
1 1 1 1 0 0 0 1
Дополнительный код числа -15: 1111 0001;Переводим дополнительный код 1100 0010 в прямой
Отлично. Теперь, по аналогии с примером выше, нам нужно перевести уже наш дополнительный код в прямой, напомню, он равен1100 0010.
Отнимаем единицу и получаем обратный код. Удобно делать так, записать дополнительный код в ответ и смотреть, к чему нужно прибавить единицу, чтобы получить такой дополнительный код. Начинаем с крайнего правого разряда и смотрим: к чему нужно прибавить 1 чтобы получить 0? К 1, получим 10, 0 уходит в ответ, а единичка на следующий разряд. Далее, что нужно прибавить к 0, чтобы получить один. Единицу, но так как мы с предыдущего разряда имеем единичку, то в ответ пишем 0. Далее, чтобы получить 0, что нужно прибавить к 0? Конечно, 0. Так еще 4 раза. И остались последние 2 разряда, где к 0 надо что-то прибавить, чтобы получить 1. Конечно, в обоих случаях нужно прибавить 1. Итого:
1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0Самое сложное позади! Мы получили инверсный (обратный) код и нам осталось получить прямой. Инвертируем все 0 на 1 и наоборот:
1100 0001- инверсный код;0011 1110- прямой код нашего числа, точнее его положительного представления;Переводим в десятичную систему счисления (Онлайн калькулятор с объяснением вычислений):
0011 1110 = 0∙2^7+0∙2^6+1∙2^5+1∙2^4+1∙2^3+1∙2^2+1∙2^1+0∙2^0 = 0+0+32+16+8+4+2+0 = 62;Осталось только добавить к числу минус и наш ответ:
-62.
Пример 2: что дает (short) 10_000_000
Таким же образом преобразуются числа типаint к типу short:
int i = 10_000_000;
short s = (short)i;
System.out.println(s); // -27008
10.000.000 в 10-ной системе счисления =
0000 0000 1001 1000 1001 0110 1000 0000в 2-ной.В Java число типа
intзанимает 4 байта, аshort- 2 байта, или 16 бит, поэтому отсекаем слева до 16 цифр:1001 0110 1000 0000.Крайний левый бит (старший бит, он же знаковый бит) получился равен 1. Значит перед нами дополнительный код отрицательного числа, поэтому переходим к следующему пункту.
Переводим в обратный код:
1 0 0 1 0 1 1 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 1 0 1 0 0 0 0 0 0 0Обратный код:
1001 0110 0111 1111;Инвертируем и получаем прямой код:
0110 1001 1000 0000.Переводим в десятичную систему счисления и получаем положительное представление нашего числа:
1∙2^14+1∙2^13+1∙2^11+1∙2^8+1∙2^7 = 16384+8192+2048+256+128 = 27008.Добавляем минус и получаем ответ:
-27008
Алгоритм: как вручную привести int к byte или short
Соберем весь разбор в один список, он подходит для любого числа:- Переведите число в двоичную систему: делите на 2 и записывайте остатки, затем прочитайте их снизу вверх.
- Оставьте младшие 8 бит для
byteили 16 бит дляshort, все биты левее отбросьте. - Посмотрите на старший из оставшихся битов. Если он 0, число положительное: переведите его в десятичную систему, и ответ готов.
- Если он 1, перед вами дополнительный код отрицательного числа: вычтите единицу и инвертируйте все биты, получится прямой код модуля числа.
- Переведите модуль в десятичную систему и припишите минус.
Вопросы и ответы
Почему (byte) 450 в Java дает -62?
Потому чтоbyte хранит только 8 бит, а при явном приведении Java отбрасывает все старшие биты (JLS 5.1.3). 450 в двоичной записи это 1 1100 0010; после отбрасывания остается 1100 0010. Старший бит здесь 1, значит число отрицательное и записано в дополнительном коде: вычитаем единицу, инвертируем биты, получаем 0011 1110 = 62, итого -62.
Что такое дополнительный код и зачем он нужен?
Это способ записи отрицательных чисел, при котором старший бит означает знак, а само число получают из положительного представления инверсией всех битов и прибавлением единицы: 15 это0000 1111, а -15 это 1111 0001. Благодаря этому процессор складывает и вычитает положительные и отрицательные числа одной и той же схемой, а ноль представлен единственным образом. Именно так Java хранит byte, short, int и long.
Как быстро посчитать результат приведения без перевода в двоичную систему?
Возьмите остаток от деления на 256 дляbyte или на 65536 для short, и если он больше 127 (для short больше 32767), вычтите 256 (65536). Для 450: 450 % 256 = 194, это больше 127, значит 194 - 256 = -62. Для 10 000 000 остаток от деления на 65536 равен 38528, и 38528 - 65536 = -27008. Для отрицательных чисел берите неотрицательный остаток (в Java его дает Math.floorMod): (byte) -450 равно 62.
Как проверить эти вычисления в Java?
Integer.toBinaryString(450) выведет 111000010, а Integer.toBinaryString(b & 0xFF), где b = (byte) 450, покажет младшие 8 бит: 11000010. Само значение печатает обычный System.out.println((byte) 450), а Byte.toUnsignedInt(b) вернет 194, то есть те же 8 бит, прочитанные как число без знака.
Почему компилятор не предупреждает о потере данных?
Явное приведение(byte) i это и есть ваше согласие на потерю: без него строка byte b = i; не скомпилируется. Исключение одно: константа, которая помещается в тип, присваивается без приведения, поэтому byte b = 100; допустимо, а byte b = 200; это ошибка компиляции (JLS, раздел 5.2). Остальные правила работы с примитивами разобраны в статье об операциях над примитивными типами.
Работает ли то же правило для long и для дробных чисел?
Для целых типов да:(int) от long, (short) и (byte) от int оставляют младшие 32, 16 или 8 бит. Для дробных иначе: при (int) 3.99 дробная часть отбрасывается и получается 3, слишком большие значения не заворачиваются по кругу, а превращаются в Integer.MAX_VALUE или Integer.MIN_VALUE, а NaN становится нулем. Если же привести double сразу к byte, Java сначала делает из него int, а потом отбрасывает старшие биты (JLS 5.1.3).
Ссылка на онлайн-калькулятор прямого, обратного и дополнительного кода. Также на этом сайте под калькулятором есть немного теории об обратном и дополнительном коде.
Читайте также:
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ