Поделиться через


Оператор беззнакового сдвига вправо

Заметка

Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Он включает предлагаемые изменения спецификаций, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.

Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия фиксируются в соответствующих собраниях по проектированию языка (LDM).

Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .

Сводка

Оператор сдвига вправо без знака будет поддерживаться C# как встроенный оператор (для примитивных целочисленных типов) и в качестве определённого пользователем оператора.

Мотивация

При работе с целочисленным значением со знаком нередко нужно сдвигать биты вправо, не копируя бит высокого порядка на каждом сдвиге. Хотя этого можно достичь для примитивных целочисленных типов с обычным оператором сдвига, требуется преобразование в неподписанный тип до операции сдвига и преобразование обратно после неё. В контексте универсальных математических интерфейсов, которые библиотеки планируют открывать, это может создать дополнительные сложности, так как тип может не иметь заранее определенного или известного аналога без знака в универсальном математическом коде, хотя алгоритм может зависеть от возможности выполнения операции сдвига вправо без учета знака.

Подробный дизайн

Операторы и пунктуаторы

Раздел §6.4.6 будет изменен, чтобы включить оператор беззнакового сдвига вправо >>>.

unsigned_right_shift
    : '>>>'
    ;

unsigned_right_shift_assignment
    : '>>>='
    ;

Между маркерами в unsigned_right_shift и unsigned_right_shift_assignment продукциях не допускается никаких символов (даже пробелов). Эти построения обрабатываются особым образом, чтобы обеспечить правильную обработку списка параметров типа .

Операторы сдвига

Раздел §12.11 будет изменен, чтобы включить оператор >>> — оператор без знака вправо:

Операторы <<, >> и >>> используются для выполнения операций переключения битов.

shift_expression
    : additive_expression
    | shift_expression '<<' additive_expression
    | shift_expression right_shift additive_expression
    | shift_expression unsigned_right_shift additive_expression
    ;

Для операции формы x << count или x >> count или x >>> countразрешение перегрузки двоичных операторов (§12.4.5) применяется для выбора конкретной реализации оператора. Операнды преобразуются в типы параметров выбранного оператора, а тип результата — возвращаемый тип оператора.

Предопределенные операторы смены без знака будут поддерживать тот же набор подписей, которые предопределенные операторы со знаком смены поддерживаются сегодня в текущей реализации.

  • Сдвиг вправо:

    int operator >>>(int x, int count);
    uint operator >>>(uint x, int count);
    long operator >>>(long x, int count);
    ulong operator >>>(ulong x, int count);
    nint operator >>>(nint x, int count);
    nuint operator >>>(nuint x, int count);
    

    Оператор >>> сдвигает x вправо на несколько битов, вычисляемых, как описано ниже.

    Младшие биты x отбрасываются, оставшиеся биты сдвигаются вправо, а позиции старших битов заполняются нулями.

Для предопределенных операторов число битов для смены вычисляется следующим образом:

  • Если тип x является int или uint, число сдвигов определяется младшими пятью битами count. Другими словами, количество сдвигов вычисляется из count & 0x1F.
  • Если тип x является long или ulong, число сдвигов задается шестью младшими битами count. Другими словами, количество сдвигов вычисляется из count & 0x3F.

Если результирующее число сдвигов равно нулю, операторы сдвига просто возвращают значение x.

Операции смены никогда не вызывают переполнения и создают те же результаты в checked и unchecked контекстах.

Операторы назначения

Раздел §12.21 будет изменен, чтобы включить unsigned_right_shift_assignment следующим образом:

assignment_operator
    : '='
    | '+='
    | '-='
    | '*='
    | '/='
    | '%='
    | '&='
    | '|='
    | '^='
    | '<<='
    | right_shift_assignment
    | unsigned_right_shift_assignment
    ;

Целочисленные типы

Целочисленные типы в разделе §8.3.6 будут изменены, чтобы включить сведения об операторе >>>. Соответствующая точка маркера является следующей:

  • Для двоичных <<операторов >> и >>> левый операнд преобразуется в тип T, где T является первым из int, uint, longи ulong, которые могут полностью представлять все возможные значения операнда. Затем операция выполняется с точностью типа T, а тип результата — T.

Константные выражения

Оператор >>> будет добавлен в набор конструкций, разрешенных в константных выражениях §12.23.

Перегрузка оператора

Оператор >>> будет добавлен в набор перегруженных двоичных операторов в §12.4.3.

Поднятые операторы

Оператор >>> будет добавлен в набор двоичных операторов с поднятой формой в §12.4.8.

Приоритет оператора и ассоциативность

Раздел §12.4.2 будет изменен, чтобы добавить оператор >>> в категорию "Сдвиг" и оператор >>>= в категорию "Присваивание и лямбда-выражение".

Неоднозначность грамматики

Оператор >>> подвергается той же неоднозначности грамматики, описанной в §6.2.5 как обычный оператор >>.

Операторы

Раздел §15.10 будет отредактирован для включения оператора >>>.

overloadable_binary_operator
    : '+'   | '-'   | '*'   | '/'   | '%'   | '&'   | '|'   | '^'   | '<<'
    | right_shift | unsigned_right_shift | '=='  | '!='  | '>'   | '<'   | '>='  | '<='
    ;

Двоичные операторы

Подпись оператора >>> применяется к тем же правилам, что и в §15.10.3 для подписи оператора >>.

Имя метаданных

Раздел "I.10.3.2 Двоичные операторы" в ECMA-335 уже зарезервирован для оператора сдвига без знака вправо - op_UnsignedRightShift.

Деревья выражений Linq

Оператор >>> не поддерживается в деревах выражений Linq, так как семантика предопределенных операторов >>> для подписанных типов не может быть точно представлена без добавления преобразований в неподписанный тип и обратно. Дополнительные сведения см. в https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md#unsigned-right-shift-operator.

Динамическая привязка

Похоже, что динамическая привязка использует элементы перечисления System.Linq.Expressions.ExpressionType для передачи информации о типе двоичного оператора привязке среды выполнения. Поскольку у нас нет элемента, специально представляющего оператор сдвига вправо без знака, динамическая привязка для оператора >>> не будет поддерживаться, и раздел статической и динамической привязки (§12.3) будет изменён, чтобы отразить этот факт.

Недостатки

Альтернативы

Деревья выражений Linq

Оператор >>> будет поддерживаться в деревьях выражений LINQ.

  • Для определяемого пользователем оператора будет создан узел BinaryExpression, указывающий на метод оператора.
  • Для предопределенных операторов
    • Когда первый операнд является беззнаковым типом, будет создан узел BinaryExpression.
    • Когда первый операнд является подписанным типом, преобразование первого операнда в неподписанный тип будет добавлено, будет создан узел BinaryExpression, и преобразование результата обратно в подписанный тип будет добавлено.

Например:

Expression<System.Func<int, int, int>> z = (x, y) => x >>> y; // (x, y) => Convert((Convert(x, UInt32) >> y), Int32)

Resolution:

Отклонено, см. https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md#unsigned-right-shift-operator для получения дополнительной информации.

Неразрешенные вопросы

Дизайнерские встречи

https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md