Когда использовать операторы Shift <<>> в С#?

Я изучал операторы сдвига в С#, пытаясь выяснить когда использовать их в моем коде.

Я нашел ответ, но для Java вы могли:

a) Сделайте быстрее целые операции умножения и деления:

* 4839534 * 4 * можно сделать следующим образом: 4839534 < 2

или

543894/2 можно сделать следующим образом: 543894 → 1

Операции сдвига намного быстрее, чем умножение для большинства процессоров.

b) Повторная сборка байтовых потоков со значениями int

c) Для ускорения операций с графикой, поскольку цвета красного, зеленого и синего цветов кодируются отдельными байтами.

d) Упаковка небольших чисел в один длинный...


Для b, c и d я не могу представить здесь настоящий образец.

Кто-нибудь знает, можем ли мы выполнить все эти пункты на С#? Существует ли более практическое использование операторов сдвига в С#?

Ответ 1

Нет необходимости использовать их в целях оптимизации, потому что компилятор позаботится об этом для вас.

Используйте их только в том случае, если смещение битов является реальным намерением вашего кода (как и в остальных примерах вашего вопроса). Остальное время просто умножайте и разделите так, чтобы читатели вашего кода могли понять это с первого взгляда.

Ответ 2

Если нет очень веских оснований, мое мнение заключается в том, что использование умных трюков, подобных обычно, делает более сложным код с небольшой добавленной стоимостью. Авторы-компиляторы - умная группа разработчиков и знают намного больше этих трюков, чем это делает средний программист. Например, деление целого на мощность 2 быстрее с оператором сдвига, чем деление, но, вероятно, это не обязательно, так как компилятор сделает это за вас. Вы можете увидеть это, посмотрев сборку, которая компилируется как компилятором Microsoft C/С++, так и gcc. Эти оптимизации.

Ответ 3

Я поделюсь интересным использованием, которое я наткнулся в прошлом. Этот пример бесстыдно скопирован из дополнительного ответа на вопрос "Что означает атрибут Enum [Flags] Enum в С#?"

[Flags]
public enum MyEnum
{
    None   = 0,
    First  = 1 << 0,
    Second = 1 << 1,
    Third  = 1 << 2,
    Fourth = 1 << 3
}

Это может быть проще расширить, чем записывать литералы 1, 2, 4, 8, ..., особенно после того, как вы пройдете мимо 17 флагов.

Компромисс заключается в том, что если вам нужно больше 31 флага (1 << 30), вам также нужно быть осторожным, чтобы указать ваше перечисление как нечто с более высокой верхней границей, чем целое число со знаком (объявив его как public enum MyEnum : ulong, например, что даст вам до 64 флагов). Это потому, что...

1 << 29 == 536870912
1 << 30 == 1073741824
1 << 31 == -2147483648
1 << 32 == 1
1 << 33 == 2

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

Как указано в ClickRick, даже если ваше перечисление происходит из ulong, ваша операция смены битов должна выполняться против ulong или ваши значения перечисления все равно будут разбиты.

[Flags]
public enum MyEnum : ulong
{
    None   = 0,
    First  = 1 << 0,
    Second = 1 << 1,
    Third  = 1 << 2,
    Fourth = 1 << 3,

    // Compiler error:
    // Constant value '-2147483648' cannot be converted to a 'ulong'
    // (Note this wouldn't be thrown if MyEnum derived from long)
    ThirtySecond = 1 << 31,

    // so what you would have to do instead is...
    ThirtySecond = 1UL << 31,
    ThirtyThird  = 1UL << 32,
    ThirtyFourth = 1UL << 33
}

Ответ 4

Ознакомьтесь с этими статьями Википедии о системе двоичных номеров и арифметический сдвиг. Я думаю, они ответят на ваши вопросы.

Операторы сдвига встречаются редко в бизнес-приложениях сегодня. Они часто появляются в низкоуровневом коде, который взаимодействует с оборудованием или манипулирует упакованными данными. Они были более распространены в дни 64-сегментных сегментов памяти.