proposals/csharp-11.0/unsigned-right-shift-operator.md
[!INCLUDESpecletdisclaimer]
Champion issue: https://github.com/dotnet/csharplang/issues/4682
An unsigned right shift operator will be supported by C# as a built-in operator (for primitive integral types) and as a user-defined operator.
When working with signed integral value, it is not uncommon that you need to shift bits right without replicating the high order bit on each shift. While this can be achieved for primitive integral types with a regular shift operator, a cast to an unsigned type before the shift operation and a cast back after it is required. Within the context of the generic math interfaces the libraries are planning to expose, this is potentially more problematic as the type might not necessary have an unsigned counterpart defined or known upfront by the generic math code, yet an algorithm might rely on ability to perform an unsigned right shift operation.
Section §6.4.6 will be adjusted
to include >>> operator - the unsigned right shift operator:
unsigned_right_shift
: '>>>'
;
unsigned_right_shift_assignment
: '>>>='
;
No characters of any kind (not even whitespace) are allowed between the tokens in unsigned_right_shift and unsigned_right_shift_assignment productions. These productions are treated specially in order to enable the correct handling of type_parameter_lists.
Section §12.11 will be adjusted
to include >>> operator - the unsigned right shift operator:
The <<, >> and >>> operators are used to perform bit shifting operations.
shift_expression
: additive_expression
| shift_expression '<<' additive_expression
| shift_expression right_shift additive_expression
| shift_expression unsigned_right_shift additive_expression
;
For an operation of the form x << count or x >> count or x >>> count, binary operator overload resolution (§12.4.5) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.
The predefined unsigned shift operators will support the same set of signatures that predefined signed shift operators support today in the current implementation.
Shift right:
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);
The >>> operator shifts x right by a number of bits computed as described below.
The low-order bits of x are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero.
For the predefined operators, the number of bits to shift is computed as follows:
x is int or uint, the shift count is given by the low-order five bits of count. In other words, the shift count is computed from count & 0x1F.x is long or ulong, the shift count is given by the low-order six bits of count. In other words, the shift count is computed from count & 0x3F.If the resulting shift count is zero, the shift operators simply return the value of x.
Shift operations never cause overflows and produce the same results in checked and unchecked contexts.
Section §12.21 will be adjusted to include unsigned_right_shift_assignment as follows:
assignment_operator
: '='
| '+='
| '-='
| '*='
| '/='
| '%='
| '&='
| '|='
| '^='
| '<<='
| right_shift_assignment
| unsigned_right_shift_assignment
;
The Integral types §8.3.6 section will be adjusted to include information about >>> operator. The relevant bullet point is the following:
<<, >> and >>> operators, the left operand is converted to type T, where T is the first of int, uint, long, and ulong that can fully represent all possible values of the operand. The operation is then performed using the precision of type T, and the type of the result is T.Operator >>> will be added to the set of constructs permitted in constant expressions at
§12.23.
Operator >>> will be added to the set of overloadable binary operators at §12.4.3.
Operator >>> will be added to the set of binary operators permitting a lifted form at §12.4.8.
Section §12.4.2 will be adjusted to add >>> operator to the "Shift" category and >>>= operator to the "Assignment and lambda expression" category.
The >>> operator is subject to the same grammar ambiguities described at §6.2.5 as a regular >> operator.
The §15.10 section will be adjusted to include >>> operator.
overloadable_binary_operator
: '+' | '-' | '*' | '/' | '%' | '&' | '|' | '^' | '<<'
| right_shift | unsigned_right_shift | '==' | '!=' | '>' | '<' | '>=' | '<='
;
The signature of a >>> operator is subject to the same rules as those at §15.10.3
for the signature of a >> operator.
Section "I.10.3.2 Binary operators" of ECMA-335 already reserved the name for an unsigned right shift operator - op_UnsignedRightShift.
The >>> operator will not be supported in Linq Expression Trees because semantics of predefined >>> operators on signed types cannot be accurately represented without adding conversions to an unsigned type and back. See https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md#unsigned-right-shift-operator for more information.
It looks like dynamic binding uses values of System.Linq.Expressions.ExpressionType enum to communicate
binary operator kind to the runtime binder. Since we don't have a member specifically representing
an unsigned right shift operator, dynamic binding for >>> operator will not be supported and the
static and dynamic binding (§12.3) section
will be adjusted to reflect that.
The >>> operator will be supported in Linq Expressioin Trees.
For example:
Expression> z = (x, y) => x >>> y; // (x, y) => Convert((Convert(x, UInt32) >> y), Int32)
Resolution:
Rejected, see https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md#unsigned-right-shift-operator for more information.
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md