Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exposing Radix and the remaining Is* generic-math APIs #69651

Merged
merged 2 commits into from
May 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/libraries/Common/tests/System/GenericMathHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ public static class NumberBaseHelper<TSelf>
{
public static TSelf One => TSelf.One;

public static int Radix => TSelf.Radix;

public static TSelf Zero => TSelf.Zero;

public static TSelf Abs(TSelf value) => TSelf.Abs(value);
Expand All @@ -310,10 +312,20 @@ public static TSelf CreateSaturating<TOther>(TOther value)
public static TSelf CreateTruncating<TOther>(TOther value)
where TOther : INumberBase<TOther> => TSelf.CreateTruncating(value);

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

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

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

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

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

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

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

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

public static bool IsNegative(TSelf value) => TSelf.IsNegative(value);
Expand All @@ -322,10 +334,18 @@ public static TSelf CreateTruncating<TOther>(TOther value)

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

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

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

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

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

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

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

public static TSelf MaxMagnitude(TSelf x, TSelf y) => TSelf.MaxMagnitude(x, y);

public static TSelf MaxMagnitudeNumber(TSelf x, TSelf y) => TSelf.MaxMagnitudeNumber(x, y);
Expand Down
14 changes: 4 additions & 10 deletions src/libraries/System.Private.CoreLib/src/System/BitConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -840,21 +840,15 @@ public static unsafe float Int32BitsToSingle(int value)
/// <param name="value">The number to convert.</param>
/// <returns>A 16-bit signed integer whose bits are identical to <paramref name="value"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe short HalfToInt16Bits(Half value)
{
return *((short*)&value);
}
public static unsafe short HalfToInt16Bits(Half value) => (short)HalfToUInt16Bits(value);

/// <summary>
/// Converts the specified 16-bit signed integer to a half-precision floating point number.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>A half-precision floating point number whose bits are identical to <paramref name="value"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Half Int16BitsToHalf(short value)
{
return *(Half*)&value;
}
public static unsafe Half Int16BitsToHalf(short value) => UInt16BitsToHalf((ushort)(value));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public static unsafe Half Int16BitsToHalf(short value) => UInt16BitsToHalf((ushort)(value));
public static unsafe Half Int16BitsToHalf(short value) => UInt16BitsToHalf((ushort)value);


/// <summary>
/// Converts the specified double-precision floating point number to a 64-bit unsigned integer.
Expand Down Expand Up @@ -899,7 +893,7 @@ public static unsafe Half Int16BitsToHalf(short value)
/// <returns>A 16-bit unsigned integer whose bits are identical to <paramref name="value"/>.</returns>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ushort HalfToUInt16Bits(Half value) => (ushort)HalfToInt16Bits(value);
public static unsafe ushort HalfToUInt16Bits(Half value) => value._value;

/// <summary>
/// Converts the specified 16-bit unsigned integer to a half-precision floating point number.
Expand All @@ -908,6 +902,6 @@ public static unsafe Half Int16BitsToHalf(short value)
/// <returns>A half-precision floating point number whose bits are identical to <paramref name="value"/>.</returns>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Half UInt16BitsToHalf(ushort value) => Int16BitsToHalf((short)value);
public static unsafe Half UInt16BitsToHalf(ushort value) => new Half(value);
}
}
30 changes: 30 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Byte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,9 @@ bool IBinaryInteger<byte>.TryWriteLittleEndian(Span<byte> destination, out int b
/// <inheritdoc cref="INumberBase{TSelf}.One" />
static byte INumberBase<byte>.One => One;

/// <inheritdoc cref="INumberBase{TSelf}.Radix" />
static int INumberBase<byte>.Radix => 2;

/// <inheritdoc cref="INumberBase{TSelf}.Zero" />
static byte INumberBase<byte>.Zero => Zero;

Expand Down Expand Up @@ -752,12 +755,27 @@ public static byte CreateTruncating<TOther>(TOther value)
}
}

/// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" />
static bool INumberBase<byte>.IsCanonical(byte value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" />
static bool INumberBase<byte>.IsComplexNumber(byte value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" />
public static bool IsEvenInteger(byte value) => (value & 1) == 0;

/// <inheritdoc cref="INumberBase{TSelf}.IsFinite(TSelf)" />
static bool INumberBase<byte>.IsFinite(byte value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" />
static bool INumberBase<byte>.IsImaginaryNumber(byte value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsInfinity(TSelf)" />
static bool INumberBase<byte>.IsInfinity(byte value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" />
static bool INumberBase<byte>.IsInteger(byte value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsNaN(TSelf)" />
static bool INumberBase<byte>.IsNaN(byte value) => false;

Expand All @@ -770,12 +788,24 @@ public static byte CreateTruncating<TOther>(TOther value)
/// <inheritdoc cref="INumberBase{TSelf}.IsNormal(TSelf)" />
static bool INumberBase<byte>.IsNormal(byte value) => value != 0;

/// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" />
public static bool IsOddInteger(byte value) => (value & 1) != 0;

/// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" />
static bool INumberBase<byte>.IsPositive(byte value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsPositiveInfinity(TSelf)" />
static bool INumberBase<byte>.IsPositiveInfinity(byte value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" />
static bool INumberBase<byte>.IsRealNumber(byte value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsSubnormal(TSelf)" />
static bool INumberBase<byte>.IsSubnormal(byte value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" />
static bool INumberBase<byte>.IsZero(byte value) => (value == 0);

/// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" />
static byte INumberBase<byte>.MaxMagnitude(byte x, byte y) => Max(x, y);

Expand Down
30 changes: 30 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,9 @@ bool IBinaryInteger<char>.TryWriteLittleEndian(Span<byte> destination, out int b
/// <inheritdoc cref="INumberBase{TSelf}.One" />
static char INumberBase<char>.One => (char)1;

/// <inheritdoc cref="INumberBase{TSelf}.Radix" />
static int INumberBase<char>.Radix => 2;

/// <inheritdoc cref="INumberBase{TSelf}.Zero" />
static char INumberBase<char>.Zero => (char)0;

Expand Down Expand Up @@ -1597,12 +1600,27 @@ static char INumberBase<char>.CreateTruncating<TOther>(TOther value)
}
}

/// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" />
static bool INumberBase<char>.IsCanonical(char value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" />
static bool INumberBase<char>.IsComplexNumber(char value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" />
static bool INumberBase<char>.IsEvenInteger(char value) => (value & 1) == 0;

/// <inheritdoc cref="INumberBase{TSelf}.IsFinite(TSelf)" />
static bool INumberBase<char>.IsFinite(char value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" />
static bool INumberBase<char>.IsImaginaryNumber(char value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsInfinity(TSelf)" />
static bool INumberBase<char>.IsInfinity(char value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" />
static bool INumberBase<char>.IsInteger(char value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsNaN(TSelf)" />
static bool INumberBase<char>.IsNaN(char value) => false;

Expand All @@ -1615,12 +1633,24 @@ static char INumberBase<char>.CreateTruncating<TOther>(TOther value)
/// <inheritdoc cref="INumberBase{TSelf}.IsNormal(TSelf)" />
static bool INumberBase<char>.IsNormal(char value) => value != 0;

/// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" />
static bool INumberBase<char>.IsOddInteger(char value) => (value & 1) != 0;

/// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" />
static bool INumberBase<char>.IsPositive(char value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsPositiveInfinity(TSelf)" />
static bool INumberBase<char>.IsPositiveInfinity(char value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" />
static bool INumberBase<char>.IsRealNumber(char value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsSubnormal(TSelf)" />
static bool INumberBase<char>.IsSubnormal(char value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" />
static bool INumberBase<char>.IsZero(char value) => (value == 0);

/// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" />
static char INumberBase<char>.MaxMagnitude(char x, char y) => (char)Math.Max(x, y);

Expand Down
58 changes: 58 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Decimal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,9 @@ public static decimal Min(decimal x, decimal y)
/// <inheritdoc cref="INumberBase{TSelf}.One" />
static decimal INumberBase<decimal>.One => One;

/// <inheritdoc cref="INumberBase{TSelf}.Radix" />
static int INumberBase<decimal>.Radix => 10;

/// <inheritdoc cref="INumberBase{TSelf}.Zero" />
static decimal INumberBase<decimal>.Zero => Zero;

Expand Down Expand Up @@ -1585,12 +1588,51 @@ public static decimal CreateTruncating<TOther>(TOther value)
}
}

/// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" />
public static bool IsCanonical(decimal value)
{
uint scale = (byte)(value._flags >> ScaleShift);

if (scale == 0)
{
// We have an exact integer represented with no trailing zero
return true;
}

// We have some value where some fractional part is specified. So,
// if the least significant digit is 0, then we are not canonical

if (value._hi32 == 0)
{
return (value._lo64 % 10) != 0;
}

var significand = new UInt128(value._hi32, value._lo64);
return (significand % 10U) != 0U;
}

/// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" />
static bool INumberBase<decimal>.IsComplexNumber(decimal value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" />
public static bool IsEvenInteger(decimal value)
{
decimal truncatedValue = Truncate(value);
return (value == truncatedValue) && ((truncatedValue._lo64 & 1) == 0);
}
dakersnar marked this conversation as resolved.
Show resolved Hide resolved

/// <inheritdoc cref="INumberBase{TSelf}.IsFinite(TSelf)" />
static bool INumberBase<decimal>.IsFinite(decimal value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" />
static bool INumberBase<decimal>.IsImaginaryNumber(decimal value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsInfinity(TSelf)" />
static bool INumberBase<decimal>.IsInfinity(decimal value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" />
public static bool IsInteger(decimal value) => value == Truncate(value);

/// <inheritdoc cref="INumberBase{TSelf}.IsNaN(TSelf)" />
static bool INumberBase<decimal>.IsNaN(decimal value) => false;

Expand All @@ -1603,12 +1645,28 @@ public static decimal CreateTruncating<TOther>(TOther value)
/// <inheritdoc cref="INumberBase{TSelf}.IsNormal(TSelf)" />
static bool INumberBase<decimal>.IsNormal(decimal value) => value != 0;

/// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" />
public static bool IsOddInteger(decimal value)
{
decimal truncatedValue = Truncate(value);
return (value == truncatedValue) && ((truncatedValue._lo64 & 1) != 0);
}
dakersnar marked this conversation as resolved.
Show resolved Hide resolved

/// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" />
public static bool IsPositive(decimal value) => value._flags >= 0;

/// <inheritdoc cref="INumberBase{TSelf}.IsPositiveInfinity(TSelf)" />
static bool INumberBase<decimal>.IsPositiveInfinity(decimal value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" />
static bool INumberBase<decimal>.IsRealNumber(decimal value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsSubnormal(TSelf)" />
static bool INumberBase<decimal>.IsSubnormal(decimal value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" />
static bool INumberBase<decimal>.IsZero(decimal value) => (value == 0);

/// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" />
public static decimal MaxMagnitude(decimal x, decimal y)
{
Expand Down
38 changes: 38 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Double.cs
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,9 @@ public static double MinNumber(double x, double y)
/// <inheritdoc cref="INumberBase{TSelf}.One" />
static double INumberBase<double>.One => One;

/// <inheritdoc cref="INumberBase{TSelf}.Radix" />
static int INumberBase<double>.Radix => 2;

/// <inheritdoc cref="INumberBase{TSelf}.Zero" />
static double INumberBase<double>.Zero => Zero;

Expand Down Expand Up @@ -1092,6 +1095,41 @@ public static double CreateTruncating<TOther>(TOther value)
return CreateSaturating(value);
}

/// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" />
static bool INumberBase<double>.IsCanonical(double value) => true;

/// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" />
static bool INumberBase<double>.IsComplexNumber(double value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" />
public static bool IsEvenInteger(double value) => IsInteger(value) && (Abs(value % 2) == 0);

/// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" />
static bool INumberBase<double>.IsImaginaryNumber(double value) => false;

/// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" />
public static bool IsInteger(double value) => IsFinite(value) && (value == Truncate(value));

/// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" />
public static bool IsOddInteger(double value) => IsInteger(value) && (Abs(value % 2) == 1);

/// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" />
public static bool IsPositive(double value) => BitConverter.DoubleToInt64Bits(value) >= 0;
dakersnar marked this conversation as resolved.
Show resolved Hide resolved

/// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" />
public static bool IsRealNumber(double value)
{
// A NaN will never equal itself so this is an
// easy and efficient way to check for a real number.

#pragma warning disable CS1718
return value == value;
#pragma warning restore CS1718
}

/// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" />
static bool INumberBase<double>.IsZero(double value) => (value == 0);

/// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" />
public static double MaxMagnitude(double x, double y) => Math.MaxMagnitude(x, y);

Expand Down
Loading