Skip to content

Commit

Permalink
int, sbyte ConvertTruncating signPropagation
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexRadch committed Sep 19, 2024
1 parent 21bb9b1 commit 47c68c6
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 3 deletions.
3 changes: 3 additions & 0 deletions src/libraries/Common/tests/System/GenericMathHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ public static TSelf CreateSaturating<TOther>(TOther value)
public static TSelf CreateTruncating<TOther>(TOther value)
where TOther : INumberBase<TOther> => TSelf.CreateTruncating(value);

public static TSelf CreateTruncating<TOther>(TOther value, bool signPropagation)
where TOther : INumberBase<TOther> => TSelf.CreateTruncating(value, signPropagation);

public static bool IsCanonical(TSelf value) => TSelf.IsCanonical(value);

public static bool IsComplexNumber(TSelf value) => TSelf.IsComplexNumber(value);
Expand Down
86 changes: 85 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Int32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,84 @@ private static bool TryConvertFromTruncating<TOther>(TOther value, out int resul
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromTruncating{TOther}(TOther, bool, out TSelf)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertFromTruncating<TOther>(TOther value, bool signPropagation, out int result) => TryConvertFromTruncating(value, signPropagation, out result);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryConvertFromTruncating<TOther>(TOther value, bool signPropagation, out int result)
where TOther : INumberBase<TOther>
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
// between signed and unsigned types, the one that handles unsigned will also
// handle `Decimal`.
//
// That is, `ConvertFrom` for `int` will handle the other signed types and
// `ConvertTo` will handle the unsigned types

if (signPropagation)
return TryConvertFromTruncating(value, out result);

if (typeof(TOther) == typeof(double))
{
double actualValue = (double)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(Half))
{
Half actualValue = (Half)(object)value;
result = (actualValue == Half.PositiveInfinity) ? MaxValue :
(actualValue == Half.NegativeInfinity) ? MinValue : (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(short))
{
ushort actualValue = (ushort)(short)(object)value;
result = actualValue;
return true;
}
else if (typeof(TOther) == typeof(long))
{
ulong actualValue = (ulong)(long)(object)value;
result = (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(Int128))
{
UInt128 actualValue = (UInt128)(Int128)(object)value;
result = (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(nint))
{
nuint actualValue = (nuint)(nint)(object)value;
result = (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(sbyte))
{
byte actualValue = (byte)(sbyte)(object)value;
result = actualValue;
return true;
}
else if (typeof(TOther) == typeof(float))
{
float actualValue = (float)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (int)actualValue;
return true;
}
else
{
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertToChecked<TOther>(int value, [MaybeNullWhen(false)] out TOther result)
Expand Down Expand Up @@ -1286,7 +1364,9 @@ static bool INumberBase<int>.TryConvertToSaturating<TOther>(int value, [MaybeNul

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertToTruncating<TOther>(int value, [MaybeNullWhen(false)] out TOther result)
static bool INumberBase<int>.TryConvertToTruncating<TOther>(int value, [MaybeNullWhen(false)] out TOther result) => TryConvertToTruncating(value, out result);

private static bool TryConvertToTruncating<TOther>(int value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1352,6 +1432,10 @@ static bool INumberBase<int>.TryConvertToTruncating<TOther>(int value, [MaybeNul
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, bool, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertToTruncating<TOther>(int value, bool signPropagation, [MaybeNullWhen(false)] out TOther result) => TryConvertToTruncating(value, out result);

//
// IParsable
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ static virtual TSelf CreateTruncating<TOther>(TOther value)
/// <summary>Creates an instance of the current type from a value, truncating any values that fall outside the representable range of the current type.</summary>
/// <typeparam name="TOther">The type of <paramref name="value" />.</typeparam>
/// <param name="value">The value which is used to create the instance of <typeparamref name="TSelf" />.</param>
/// <param name="signPropagation"></param>
/// <returns>An instance of <typeparamref name="TSelf" /> created from <paramref name="value" />, truncating if <paramref name="value" /> falls outside the representable range of <typeparamref name="TSelf" />.</returns>
/// <exception cref="NotSupportedException"><typeparamref name="TOther" /> is not supported.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -414,7 +415,9 @@ protected static virtual bool TryConvertFromTruncating<TOther>(TOther value, boo
{
if (signPropagation || !TOther.IsNegative(value))
return TSelf.TryConvertFromTruncating(value, out result);
throw new NotImplementedException();

result = default;
return false;
}

/// <summary>Tries to convert an instance of the current type to another type, throwing an overflow exception for any values that fall outside the representable range of the current type.</summary>
Expand Down Expand Up @@ -461,8 +464,9 @@ protected static virtual bool TryConvertToTruncating<TOther>(TSelf value, bool s
{
if (signPropagation || !TSelf.IsNegative(value))
return TSelf.TryConvertToTruncating(value, out result);
throw new NotImplementedException();

result = default;
return false;
}

/// <summary>Tries to parse a string into a value.</summary>
Expand Down
152 changes: 152 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/SByte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,84 @@ private static bool TryConvertFromTruncating<TOther>(TOther value, out sbyte res
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromTruncating{TOther}(TOther, bool, out TSelf)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<sbyte>.TryConvertFromTruncating<TOther>(TOther value, bool signPropagation, out sbyte result) => TryConvertFromTruncating(value, signPropagation, out result);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryConvertFromTruncating<TOther>(TOther value, bool signPropagation, out sbyte result)
where TOther : INumberBase<TOther>
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
// between signed and unsigned types, the one that handles unsigned will also
// handle `Decimal`.
//
// That is, `ConvertFrom` for `sbyte` will handle the other signed types and
// `ConvertTo` will handle the unsigned types

if (signPropagation)
return TryConvertFromTruncating(value, out result);

if (typeof(TOther) == typeof(double))
{
double actualValue = (double)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (sbyte)actualValue;
return true;
}
else if (typeof(TOther) == typeof(Half))
{
Half actualValue = (Half)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (sbyte)actualValue;
return true;
}
else if (typeof(TOther) == typeof(short))
{
ushort actualValue = (ushort)(short)(object)value;
result = (sbyte)actualValue;
return true;
}
else if (typeof(TOther) == typeof(int))
{
uint actualValue = (uint)(int)(object)value;
result = (sbyte)actualValue;
return true;
}
else if (typeof(TOther) == typeof(long))
{
ulong actualValue = (ulong)(long)(object)value;
result = (sbyte)actualValue;
return true;
}
else if (typeof(TOther) == typeof(Int128))
{
UInt128 actualValue = (UInt128)(Int128)(object)value;
result = (sbyte)actualValue;
return true;
}
else if (typeof(TOther) == typeof(nint))
{
nuint actualValue = (nuint)(nint)(object)value;
result = (sbyte)actualValue;
return true;
}
else if (typeof(TOther) == typeof(float))
{
float actualValue = (float)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (sbyte)actualValue;
return true;
}
else
{
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<sbyte>.TryConvertToChecked<TOther>(sbyte value, [MaybeNullWhen(false)] out TOther result)
Expand Down Expand Up @@ -1206,6 +1284,9 @@ static bool INumberBase<sbyte>.TryConvertToSaturating<TOther>(sbyte value, [Mayb
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<sbyte>.TryConvertToTruncating<TOther>(sbyte value, [MaybeNullWhen(false)] out TOther result)
=> TryConvertToTruncating(value, out result);

private static bool TryConvertToTruncating<TOther>(sbyte value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1271,6 +1352,77 @@ static bool INumberBase<sbyte>.TryConvertToTruncating<TOther>(sbyte value, [Mayb
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, bool, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<sbyte>.TryConvertToTruncating<TOther>(sbyte value, bool signPropagation, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
// between signed and unsigned types, the one that handles unsigned will also
// handle `Decimal`.
//
// That is, `ConvertFrom` for `sbyte` will handle the other signed types and
// `ConvertTo` will handle the unsigned types

if (signPropagation)
return TryConvertToTruncating(value, out result);

if (typeof(TOther) == typeof(byte))
{
byte actualResult = (byte)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(char))
{
char actualResult = (char)(byte)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(decimal))
{
decimal actualResult = (byte)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(ushort))
{
ushort actualResult = (byte)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(uint))
{
uint actualResult = (byte)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(ulong))
{
ulong actualResult = (byte)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(UInt128))
{
UInt128 actualResult = (byte)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(nuint))
{
nuint actualResult = (byte)value;
result = (TOther)(object)actualResult;
return true;
}
else
{
result = default;
return false;
}
}

//
// IParsable
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,18 @@ public static void CreateTruncatingFromSByteTest()
Assert.Equal((int)0x0000007F, NumberBaseHelper<int>.CreateTruncating<sbyte>(0x7F));
Assert.Equal(unchecked((int)0xFFFFFF80), NumberBaseHelper<int>.CreateTruncating<sbyte>(unchecked((sbyte)0x80)));
Assert.Equal(unchecked((int)0xFFFFFFFF), NumberBaseHelper<int>.CreateTruncating<sbyte>(unchecked((sbyte)0xFF)));

Assert.Equal((int)0x00000000, NumberBaseHelper<int>.CreateTruncating<sbyte>(0x00, true));
Assert.Equal((int)0x00000001, NumberBaseHelper<int>.CreateTruncating<sbyte>(0x01, true));
Assert.Equal((int)0x0000007F, NumberBaseHelper<int>.CreateTruncating<sbyte>(0x7F, true));
Assert.Equal(unchecked((int)0xFFFFFF80), NumberBaseHelper<int>.CreateTruncating<sbyte>(unchecked((sbyte)0x80), true));
Assert.Equal(unchecked((int)0xFFFFFFFF), NumberBaseHelper<int>.CreateTruncating<sbyte>(unchecked((sbyte)0xFF), true));

Assert.Equal((int)0x00000000, NumberBaseHelper<int>.CreateTruncating<sbyte>(0x00, false));
Assert.Equal((int)0x00000001, NumberBaseHelper<int>.CreateTruncating<sbyte>(0x01, false));
Assert.Equal((int)0x0000007F, NumberBaseHelper<int>.CreateTruncating<sbyte>(0x7F, false));
Assert.Equal(unchecked((int)0x80), NumberBaseHelper<int>.CreateTruncating<sbyte>(unchecked((sbyte)0x80), false));
Assert.Equal(unchecked((int)0xFF), NumberBaseHelper<int>.CreateTruncating<sbyte>(unchecked((sbyte)0xFF), false));
}

[Fact]
Expand Down

0 comments on commit 47c68c6

Please sign in to comment.