-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Background and motivation
I want to calculate the difference between two values as a non-negative max - start number.
TFrom start, and TFrom max define a range whose length is from 0 to int.MaxValue - 1.
0 <= length < int.MaxValue
When TFrom is wider than int, calculate the length simply as length = int.CreateTruncating(end - start), because the difference end - start is always positive.
When TFrom is narrower than int and has a sign, the difference between them can be a negative number, which is difficult to convert to a positive value. For example when TFrom is sbyte and start = -128; max = 127 then length = 128 + 127 = 255 = -1 (signed byte).
It isn't not easy to convert TFrom(-1) to positive TTo(255)
int.CreateTruncating(TFrom(-1)) creates int(-1)
uint.CreateTruncating(TFrom(-1)) creates uint.MaxValue. This made the sign propagation!
So I made the next workaround code.
internal static int StartMaxCount(T start, T max)
{
int count = int.CreateTruncating(max - start);
if (count < 0)
count = int.CreateTruncating(max) - int.CreateTruncating(start);
return count;
}API Proposal
namespace System.Numerics
{
public interface INumberBase<TSelf> where TSelf : INumberBase<TSelf>?
{
static TSelf CreateTruncating<TOther>(TOther value, bool signPropagation) where TOther : INumberBase<TOther>;
protected static bool TryConvertFromTruncating<TOther>(TOther value, bool signPropagation, [MaybeNullWhen(false)] out TSelf result) where TOther : INumberBase<TOther>;
protected static bool TryConvertToTruncating<TOther>(TSelf value, bool signPropagation, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase<TOther>;
}
}API Usage
// Range where TFrom is sbyte
TFrom start = -128;
TFrom max = 127;
// Calculate the correct length
int length = int.CreateTruncating(max - start, false);See also
runtime/src/libraries/System.Linq/src/System/Linq/Range.cs
Lines 30 to 49 in e689e2a
| public static IEnumerable<T> Range<T>(T start, int count) where T : IBinaryInteger<T> | |
| { | |
| if (count < 0) | |
| { | |
| ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); | |
| } | |
| if (count == 0) | |
| { | |
| return []; | |
| } | |
| T max = start + T.CreateTruncating(count - 1); | |
| if (start > max || RangeIterator<T>.StartMaxCount(start, max) + 1 != count) | |
| { | |
| ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); | |
| } | |
| return new RangeIterator<T>(start, count); | |
| } |
Alternative Designs
No response
Risks
No response