Skip to content

[API Proposal]: Support General Math value creation without sign propagation #106927

Closed
@AlexRadch

Description

@AlexRadch

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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Numericsneeds-author-actionAn issue or pull request that requires more info or actions from the author.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions