Есть ли разница в способах преобразования из целого числа в целое?

Есть ли разница, когда я хочу преобразовать целое число (например, 32-разрядное целое число / int) в другой тип целого числа (например, 8-разрядное целое число / байт). Вот пример кода для двух способов, которыми я могу его преобразовать:

byte foo(int value)
{
//return value; <-- this causes problems because I need to convert it to byte

//First way(most people use this):
return (byte)value; //this involves casting the value and also works if value is floating point type

//Second way:
return value & byte.MaxValue; //byte.MaxValue is a constant that is 255
}

Так есть ли разница между ними? Я знаю, что побитовые операции работают только для целочисленных типов. Я знаю, что второй способ не совсем читабелен или не рекомендуется. Кроме того, есть ли разница в выходе обоих способов. Это не только для int и byte, но и для каждой комбинации целочисленных типов.

Итак, похоже, что эта операция по-разному ведет себя на разных языках. Я не хочу видеть различия, поэтому, пожалуйста, отправьте ответы для C ++ / C # / D.

Также я забыл, что имел в виду только целые числа без знака (без знака). Так же и для всех целочисленных типов без знака.

0

Решение

В C # приведение int к байту вызовет исключение, если оно выходит за пределы диапазона а также в пределах checked контекст. В противном случае, приведение в действие действует почти так же, как в C ++.

Продвижение типа работает в C # так же, как в C ++ (как описано Марком Б).

Для сравнения посмотрите на IL, сгенерированный этими тремя методами:

byte foo1(uint value)
{
return (byte) value;
}

.method private hidebysig instance uint8 foo1(int32 'value') cil managed
{
.maxstack 8
L_0000: ldarg.1
L_0001: conv.u1
L_0002: ret
}

Против

byte foo2(uint value)
{
checked
{
return (byte)value;
}
}

.method private hidebysig instance uint8 foo2(uint32 'value') cil managed
{
.maxstack 8
L_0000: ldarg.1
L_0001: conv.ovf.u1.un
L_0002: ret
}

А для АНДИНГА:

byte foo3(int value)
{
return (byte)(value & byte.MaxValue);
}

.method private hidebysig instance uint8 foo3(uint32 'value') cil managed
{
.maxstack 8
L_0000: ldarg.1
L_0001: ldc.i4 255
L_0006: and
L_0007: conv.u1
L_0008: ret
}

Это снова использует conv.u1Как и в первом методе, все, что он делает, — это вводит накладные расходы на отключение дополнительных битов, которые игнорируются conv.u1 инструкция в любом случае.

Поэтому в C # я бы просто использовал приведение, если вас не волнует проверка диапазона.

Одна интересная вещь заключается в том, что в C # это даст вам ошибку компилятора:

Trace.Assert(((byte)256) == 0); // Compiler knows 256 is out of range.

Это не даст ошибку компиляции:

int value = 256;
Trace.Assert(((byte)value) == 0); // Compiler doesn't care.

И, конечно, это не даст ошибку компиляции:

unchecked
{
Trace.Assert(((byte)256) == 0);
}

Странно, что первый выдает ошибку компилятора, хотя по умолчанию он не проверяется во время выполнения. Я думаю, время компиляции проверено по умолчанию!

3

Другие решения

В C ++ существует абсолютная разница в том, как получается результат, потому что операнды & будет повышен до размера большего из двух типов. Если значение «макс» вы & с случаем будет подписан, тогда вы подпишете расширение, и побитовая операция вряд ли будет иметь желаемый эффект.

Я бы лично предпочел явное return static_cast<char>(value);

0