Skip to content

Commit

Permalink
Exposing Radix and the remaining Is* generic-math APIs (#69651)
Browse files Browse the repository at this point in the history
* Exposing Radix and the remaining Is* generic-math APIs

* Adding tests for Radix and the remaining Is* generic-math APIs
  • Loading branch information
tannergooding authored May 23, 2022
1 parent dfedca1 commit f6fe0f8
Show file tree
Hide file tree
Showing 46 changed files with 4,862 additions and 530 deletions.
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));

/// <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);
}

/// <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);
}

/// <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;

/// <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

0 comments on commit f6fe0f8

Please sign in to comment.