namespace System.Numerics
{
public partial interface IBinaryInteger<TSelf>
{
static virtual TSelf ResetLowestSetBit(TSelf value);
static virtual TSelf SetBit(TSelf value, TSelf index);
static virtual TSelf ClearBit(TSelf value, TSelf index);
static virtual TSelf ToggleBit(TSelf value, TSelf index);
}
}
// Public/implicit on all types except char (explicitly special handled), and BigInteger (either explicit or via DIM)
// Same as ReverseBits in https://github.com/dotnet/runtime/issues/125879#issuecomment-4433166525
namespace System
{
public readonly partial struct Byte : IBinaryInteger<byte>
{
public static byte ResetLowestSetBit(byte value);
public static byte SetBit(byte value, byte index);
public static byte ClearBit(byte value, byte index);
public static byte ToggleBit(byte value, byte index);
}
public readonly partial struct SByte : IBinaryInteger<sbyte>
{
public static sbyte ResetLowestSetBit(sbyte value);
public static sbyte SetBit(sbyte value, sbyte index);
public static sbyte ClearBit(sbyte value, sbyte index);
public static sbyte ToggleBit(sbyte value, sbyte index);
}
public readonly partial struct UInt16 : IBinaryInteger<ushort>
{
public static ushort ResetLowestSetBit(ushort value);
public static ushort SetBit(ushort value, ushort index);
public static ushort ClearBit(ushort value, ushort index);
public static ushor tToggleBit(ushort value, ushort index);
}
public readonly partial struct Int16 : IBinaryInteger<short>
{
public static short ResetLowestSetBit(short value);
public static short SetBit(short value, short index);
public static short ClearBit(short value, short index);
public static short ToggleBit(short value, short index);
}
public readonly partial struct UInt32 : IBinaryInteger<uint>
{
public static uint ResetLowestSetBit(uint value);
public static uint SetBit(uint value, uint index);
public static uint ClearBit(uint value, uint index);
public static uint ToggleBit(uint value, uint index);
}
public readonly partial struct Int32 : IBinaryInteger<int>
{
public static int ResetLowestSetBit(int value);
public static int SetBit(int value, int index);
public static int ClearBit(int value, int index);
public static int ToggleBit(int value, int index);
}
public readonly partial struct UInt64 : IBinaryInteger<ulong>
{
public static ulong ResetLowestSetBit(ulong value);
public static ulong SetBit(ulong value, ulong index);
public static ulong ClearBit(ulong value, ulong index);
public static ulong ToggleBit(ulong value, ulong index);
}
public readonly partial struct Int64 : IBinaryInteger<long>
{
public static long ResetLowestSetBit(long value);
public static long SetBit(long value, long index);
public static long ClearBit(long value, long index);
public static long ToggleBit(long value, long index);
}
public readonly partial struct UIntPtr : IBinaryInteger<nuint>
{
public static nuint ResetLowestSetBit(nuint value);
public static nuint SetBit(nuint value, nuint index);
public static nuint ClearBit(nuint value, nuint index);
public static nuint ToggleBit(nuint value, nuint index);
}
public readonly partial struct IntPtr : IBinaryInteger<nint>
{
public static nint ResetLowestSetBit(nint value);
public static nint SetBit(nint value, nint index);
public static nint ClearBit(nint value, nint index);
public static nint ToggleBit(nint value, nint index);
}
public readonly partial struct UInt128 : IBinaryInteger<UInt128>
{
public static UInt128 ResetLowestSetBit(UInt128 value);
public static UInt128 SetBit(UInt128 value, UInt128 index);
public static UInt128 ClearBit(UInt128 value, UInt128 index);
public static UInt128 ToggleBit(UInt128 value, UInt128 index);
}
public readonly partial struct Int128 : IBinaryInteger<Int128>
{
public static Int128 ResetLowestSetBit(Int128 value);
public static Int128 SetBit(Int128 value, Int128 index);
public static Int128 ClearBit(Int128 value, Int128 index);
public static Int128 ToggleBit(Int128 value, Int128 index);
}
public readonly partial struct Char : IBinaryInteger<char>
{
static char IBinaryInteger<char>.ResetLowestSetBit(char value);
static char IBinaryInteger<char>.SetBit(char value, char index);
static char IBinaryInteger<char>.ClearBit(char value, char index);
static char IBinaryInteger<char>.ToggleBit(char value, char index);
}
}
Background and motivation
The JIT already recognizes the
value & (value - 1)pattern and replaces it with the cheap instructions, but having a named helper is easier to read. This currently exists in CoreLib asBitOperations.ResetLowestSetBit(next toTrailingZeroCount,LeadingZeroCount, etc.).API Proposal
API Usage
do { uint bit = uint.TrailingZeroCount(value); // Do stuff with the bit - value &= value - 1; + value = uint.ResetLowestSetBit(value); } while (value != 0);Alternative Designs
Expose the helpers on
BitOperationsinstead.Or also expose a bunch of other bit helpers, e.g.
SetBit,ClearBit,ToggleBitProposal for `ResetLowestSetBit`, `SetBit`, `ClearBit`, `ToggleBit`
Risks
No response