Что происходит, когда вы бросаете от короткого до байтового в C #? - PullRequest
11 голосов
/ 28 сентября 2011

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

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

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

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

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

Ответы [ 10 ]

15 голосов
/ 28 сентября 2011

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
        }   
    }
}
6 голосов
/ 28 сентября 2011

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

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

  • В проверенном контексте преобразование завершается успешно, если значение исходного операнда находится в диапазоне типа назначения, но выбрасываетSystem.OverflowException, если значение исходного операнда находится вне диапазона целевого типа.
  • В неконтролируемом контексте преобразование всегда завершается успешно и выполняется следующим образом.
    • Если тип источника больше, чем тип назначения, то значение источника усекается путем отбрасывания его «дополнительных» старших значащих битов.Затем результат обрабатывается как значение типа назначения.
    • Если тип источника меньше, чем тип назначения, то значение источника либо с расширением знака, либо с расширением нуля, так что этотот же размер, что и тип назначения.Расширение знака используется, если тип источника подписан;нулевое расширение используется, если тип источника не подписан.Затем результат обрабатывается как значение типа назначения.
    • Если тип источника имеет тот же размер, что и тип назначения, то значение источника обрабатывается как значение типа назначения.
4 голосов
/ 28 сентября 2011

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

byte b = (byte)(value & 255);
3 голосов
/ 28 сентября 2011

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

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

Console.WriteLine("0x{0:X}", myByte); // 0x8C or 140
1 голос
/ 28 сентября 2011

Это похоже на то, что если у вас есть двузначное число «97» и преобразовать его в однозначное число, вы теряете 9 и сохраняете только «7»

1 голос
/ 28 сентября 2011

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

1 голос
/ 28 сентября 2011

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

1 голос
/ 28 сентября 2011

Результат тот же, когда вы делаете:

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

Все, что выше восьмибитного, просто выбрасывается. Младшие восемь битов 23948 (0x5D8C) равны 140 (0x8C).

1 голос
/ 28 сентября 2011

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

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

1 голос
/ 28 сентября 2011

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

...