Что происходит, когда вы отбрасываете от short to byte в С#?

У меня есть следующий код:

short myShort = 23948;
byte myByte = (byte)myShort;

Теперь я не ожидал, что myByte будет содержать значение 23948. Я бы предположил, что он будет содержать 255 (я считаю, что наибольшее значение для байта).

Однако он содержит 140, и это заставило меня задаться вопросом, почему; что на самом деле происходит за кулисами?

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

Ответ 1

Short - это 2-байтовый тип, а байт - это один байт. Когда вы отбрасываете два байта на один, вы вынуждаете систему делать вещи подходящими, а один из исходных байтов (наиболее значимых) получает отбрасывание и данные теряются. То, что осталось от значения 23948 (двоичный: 0101 1101 1000 1100), равно 140, который в двоичном выражении переводится в 1000 1100. Таким образом, вы переходите от:

0101 1101 1000 1100 (2 byte decimal value 23948)

в

          1000 1100 (1 byte decimal value 140)

Вы можете сделать это только с явным приведением. Если вы попытались присвоить короткий байт без броска, компилятор выкинул бы ошибку из-за возможности потери данных:

Невозможно неявно преобразовать тип 'short' в 'byte'. Явный конверсия существует (вам не хватает роли?)

Если вы отбрасываете с байта на короткий, с другой стороны, вы можете делать это неявно, поскольку никакие данные не будут потеряны.

using System;
public class MyClass
{
    public static void Main()
    {
        short myShort = 23948;
        byte myByte = (byte)myShort; // ok
        myByte = myShort; // error: 

        Console.WriteLine("Short: " + myShort);
        Console.WriteLine("Byte:  " + myByte);

        myShort = myByte; // ok

        Console.WriteLine("Short: " + myShort);
    }
}

С арифметическим переполнением и непроверенным контекстом:

using System;
public class MyClass {
    public static void Main() {
        unchecked {
            short myShort = 23948;
            byte myByte = (byte)myShort; // ok
            myByte = myShort; // still an error
            int x = 2147483647 * 2; // ok since unchecked
        }   
    }
}

Ответ 2

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

Для преобразования от интегрального типа к другому интегральному типу обработка зависит от контекста проверки переполнения (§7.6.12), в котором происходит преобразование:

  • В проверенном контексте преобразование завершается успешно, если значение исходного операнда находится в пределах целевого типа, но генерирует исключение System.OverflowException, если значение исходного операнда находится за пределами диапазона целевого типа.
  • В неконтролируемом контексте преобразование всегда выполняется успешно и выполняется следующим образом.
    • Если тип источника больше целевого типа, то исходное значение усекается путем отбрасывания его "дополнительных" наиболее значимых бит. Затем результат обрабатывается как значение типа назначения.
    • Если тип источника меньше, чем тип адресата, то исходное значение либо расширено, либо равно нулю, так что оно будет такого же размера, как и тип назначения. Расширение знака используется, если тип источника подписан; zero-extension используется, если тип источника неподписан. Затем результат обрабатывается как значение типа назначения.
    • Если тип источника имеет тот же размер, что и тип адресата, то исходное значение рассматривается как значение типа назначения.

Ответ 3

В вашем конкретном случае поведение довольно вырезано и сухо, когда вы смотрите на биты для значения:

short myShort = 0x5D8C; // 23948
byte myByte = (byte)myShort; // myShort & 0xFF

Console.WriteLine("0x{0:X}", myByte); // 0x8C or 140

Ответ 4

Это зависит; в контексте checked вы получите большое толстое исключение; в контексте unchecked (по умолчанию) вы получаете, чтобы сохранить данные из последнего байта, так же, как если бы вы сделали:

byte b = (byte)(value & 255);

Ответ 5

Сохраняются только последние 8 бит. 23948 в двоичном состоянии - 101110110001100b. Последние 8 бит составляют 10001100b, что равно 140.

Ответ 6

Когда вы вводите целочисленный тип в "меньший" целочисленный тип, учитываются только младшие биты веса. Математически, как будто вы использовали операцию modulo. Таким образом, вы получаете значение 140, потому что 23948 по модулю 256 равно 140.

Приведение длинного к int будет использовать тот же механизм.

Ответ 7

Результат будет таким же:

byte myByte = (byte)(myShort & 0xFF);

Все выше восьми бит просто выброшено. Нижние восемь бит 23948 (0x5D8C) составляют 140 (0x8C).

Ответ 8

Uhm... потому что когда вы вводите короткий (2 байта) в байт (1 байт), он получает только первый байт, а первый байт 23948 представляет 140.

Ответ 9

23948% 256 = 140, наиболее значимые байты были потеряны после преобразования, поэтому выход равен 140

Ответ 10

Например, когда у вас есть двухзначное число "97" и конвертируйте его в однозначное число, вы теряете 9 и сохраняете только "7"