С#: результат литья отрицательного целого числа в байт
Я смотрел исходный код проекта, и я заметил следующее выражение ( оба keyByte и codedByte имеют тип byte
):
return (byte)(keyByte - codedByte);
Теперь я пытаюсь понять, что будет результатом в случаях, когда keyByte меньше, чем codeodedByte, что приводит к отрицательному целому числу.
После некоторых экспериментов, чтобы понять результат литья отрицательного целого числа, имеющего значение в диапазоне [-255: -1], я получил следующие результаты:
byte result = (byte) (-6); // result = 250
byte result = (byte) (-50); // result = 206
byte result = (byte) (-17); // result = 239
byte result = (byte) (-20); // result = 236
Итак, при условии, что -256 < a < 0
, я смог определить результат:
result = 256 + a;
Мой вопрос: должен ли я всегда ожидать, что это так?
Ответы
Ответ 1
Да, это всегда будет иметь место (т.е. оно не просто зависит от вашей среды или компилятора, но определяется как часть спецификации языка С#). См. http://msdn.microsoft.com/en-us/library/aa691349(v=vs.71).aspx:
В контексте unchecked
результат усекает, отбрасывая любые старшие биты, которые не соответствуют типу назначения.
Следующий вопрос: если вы отбираете старшие биты отрицательного int
между -256 и -1 и читаете его как байт, что вы получаете? Это то, что вы уже обнаружили с помощью экспериментов: это 256 + x.
Обратите внимание, что endianness не имеет значения, потому что мы отбрасываем старшие (или наиболее значимые) биты, а не "первые" 24 бита. Поэтому, независимо от того, с какого конца мы его взяли, у нас остался младший байт, который составил этот int.
Ответ 2
Да. Помните, что в домене .Net "Byte" нет такой вещи, как "-":
http://msdn.microsoft.com/en-us/library/e2ayt412.aspx
Поскольку Byte является неподписанным типом, он не может представлять отрицательный номер. Если вы используете унарный оператор минус (-) в выражении, которое вычисляет значение типа Byte, Visual Basic преобразует выражение в Short первый. (Примечание: замените язык CLR/.Net на "Visual Basic" )
ДОПОЛНЕНИЕ:
Вот пример приложения:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestByte
{
class Program
{
static void Main(string[] args)
{
for (int i = -255; i < 256; i++)
{
byte b = (byte)i;
System.Console.WriteLine("i={0}, b={1}", i, b);
}
}
}
}
И вот результат:
testbyte|more
i=-255, b=1
i=-254, b=2
i=-253, b=3
i=-252, b=4
i=-251, b=5
...
i=-2, b=254
i=-1, b=255
i=0, b=0
i=1, b=1
...
i=254, b=254
i=255, b=255
Ответ 3
Вот алгоритм, который выполняет ту же логику, что и приведение в байт, чтобы помочь вам понять:
Для положительных результатов:
byte bNum = iNum % 256;
Для негативов:
byte bNum = 256 + (iNum % 256);
Это похоже на поиск любого k
, который вызывает x + 255k
в диапазоне 0 ... 255
. Может быть только один k
, который дает результат с этим диапазоном, и результат будет результатом кастинга в байт.
Другой способ взглянуть на это так, как будто он "циклически перемещается вокруг диапазона значений байтов":
Давайте снова используем iNum = -712
и определим a bNum = 0
.
Мы будем делать iNum++; bNum--;
до iNum == 0
:
iNum = -712;
bNum = 0;
iNum++; // -711
bNum--; // 255 (cycles to the maximum value)
iNum++; // -710
bNum--; // 254
... // And so on, as if the iNum value is being *consumed* within the byte value range cycle.
Это, конечно, просто иллюстрация, чтобы увидеть, насколько логически это работает.
Ответ 4
Это то, что происходит в контексте unchecked
. Можно сказать, что время выполнения (или компилятор, если Int32
, который вы передали в Byte
, известно в compiletime) добавляет или вычитает 256 столько раз, сколько необходимо, пока не найдет отображаемое значение.
В контексте checked
возникает исключение (или ошибка компиляции). См. http://msdn.microsoft.com/en-us/library/khy08726.aspx
Ответ 5
Да - если вы не получите исключение.
.NET определяет все арифметические операции только с 4 байтами и большими типами данных. Таким образом, единственная неочевидная точка заключается в том, как работает преобразование int
в byte
.
Для преобразования от интегрального типа к другому интегральному типу результат преобразования зависит от контекста проверки переполнения (см. стандарт ECMA 334, раздел 13.2.1).
Итак, в следующем контексте
checked
{
return (byte)(keyByte - codedByte);
}
вы увидите System.OverflowException
. Если в следующем контексте:
unchecked
{
return (byte)(keyByte - codedByte);
}
вам гарантировано всегда видеть результаты, которые вы ожидаете, независимо от того, используете ли вы или не добавляете кратное 256 разнице; например, 2 - 255 = 3.
Это верно, независимо от того, как аппаратное обеспечение представляет значения со знаком. Стандарт CLR (ECMA 335) указывает в разделе 12.1, что тип Int32
представляет собой "32-битное двоичное значение с дополнением". (Ну, это также соответствует всем платформам, на которых в настоящее время доступна .NET или моно, поэтому можно почти догадаться, что это будет работать в любом случае, но хорошо знать, что эта практика поддерживается стандартом языка и переносимым.)
Некоторые команды не хотят явно указывать контексты проверки переполнения, потому что у них есть политика проверки переполнения в начале цикла разработки, но не в выпущенном коде. В этих случаях вы можете смело выполнять байтовую арифметику следующим образом:
return (byte)((keyByte - codedByte) % 256);