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

Iterative generic math changes to match API review #69391

Merged
merged 11 commits into from
May 19, 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
18 changes: 12 additions & 6 deletions src/libraries/Common/tests/System/GenericMathHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ public static class BinaryIntegerHelper<TSelf>
{
public static (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right) => TSelf.DivRem(left, right);

public static int GetByteCount(TSelf value) => value.GetByteCount();

public static long GetShortestBitLength(TSelf value) => value.GetShortestBitLength();

public static TSelf LeadingZeroCount(TSelf value) => TSelf.LeadingZeroCount(value);

public static TSelf PopCount(TSelf value) => TSelf.PopCount(value);
Expand All @@ -39,6 +35,12 @@ public static class BinaryIntegerHelper<TSelf>

public static TSelf TrailingZeroCount(TSelf value) => TSelf.TrailingZeroCount(value);

public static int GetByteCount(TSelf value) => value.GetByteCount();

public static int GetShortestBitLength(TSelf value) => value.GetShortestBitLength();

public static bool TryWriteBigEndian(TSelf value, Span<byte> destination, out int bytesWritten) => value.TryWriteBigEndian(destination, out bytesWritten);

public static bool TryWriteLittleEndian(TSelf value, Span<byte> destination, out int bytesWritten) => value.TryWriteLittleEndian(destination, out bytesWritten);
}

Expand Down Expand Up @@ -103,14 +105,18 @@ public static class FloatingPointHelper<TSelf>
{
public static int GetExponentByteCount(TSelf value) => value.GetExponentByteCount();

public static long GetExponentShortestBitLength(TSelf value) => value.GetExponentShortestBitLength();
public static int GetExponentShortestBitLength(TSelf value) => value.GetExponentShortestBitLength();

public static int GetSignificandByteCount(TSelf value) => value.GetSignificandByteCount();

public static long GetSignificandBitLength(TSelf value) => value.GetSignificandBitLength();
public static int GetSignificandBitLength(TSelf value) => value.GetSignificandBitLength();

public static bool TryWriteExponentBigEndian(TSelf value, Span<byte> destination, out int bytesWritten) => value.TryWriteExponentBigEndian(destination, out bytesWritten);

public static bool TryWriteExponentLittleEndian(TSelf value, Span<byte> destination, out int bytesWritten) => value.TryWriteExponentLittleEndian(destination, out bytesWritten);

public static bool TryWriteSignificandBigEndian(TSelf value, Span<byte> destination, out int bytesWritten) => value.TryWriteSignificandBigEndian(destination, out bytesWritten);

public static bool TryWriteSignificandLittleEndian(TSelf value, Span<byte> destination, out int bytesWritten) => value.TryWriteSignificandLittleEndian(destination, out bytesWritten);
}

Expand Down
20 changes: 19 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Byte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,29 @@ object IConvertible.ToType(Type type, IFormatProvider? provider)
public static byte TrailingZeroCount(byte value) => (byte)(BitOperations.TrailingZeroCount(value << 24) - 24);

/// <inheritdoc cref="IBinaryInteger{TSelf}.GetShortestBitLength()" />
long IBinaryInteger<byte>.GetShortestBitLength() => (sizeof(byte) * 8) - LeadingZeroCount(m_value);
int IBinaryInteger<byte>.GetShortestBitLength() => (sizeof(byte) * 8) - LeadingZeroCount(m_value);

/// <inheritdoc cref="IBinaryInteger{TSelf}.GetByteCount()" />
int IBinaryInteger<byte>.GetByteCount() => sizeof(byte);

/// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteBigEndian(Span{byte}, out int)" />
bool IBinaryInteger<byte>.TryWriteBigEndian(Span<byte> destination, out int bytesWritten)
{
if (destination.Length >= sizeof(byte))
{
byte value = m_value;
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value);
Copy link
Member

@stephentoub stephentoub May 16, 2022

Choose a reason for hiding this comment

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

Does this result in better codegen than just destination[0] = m_value;? I'd have expected the guard above (which could also be if (!destination.IsEmpty)) to remove the bounds check.

Copy link
Member Author

Choose a reason for hiding this comment

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

Probably not, but its simpler to implement and compare the code when they are all roughly the same size/shape so I've mostly just been doing write once, copy/paste, small tweak.

I can update if you'd prefer it do the simpler thing here.


bytesWritten = sizeof(byte);
return true;
}
else
{
bytesWritten = 0;
return false;
}
}

/// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteLittleEndian(Span{byte}, out int)" />
bool IBinaryInteger<byte>.TryWriteLittleEndian(Span<byte> destination, out int bytesWritten)
{
Expand Down
60 changes: 37 additions & 23 deletions src/libraries/System.Private.CoreLib/src/System/Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1166,29 +1166,47 @@ public static int ConvertToUtf32(string s, int index)
//

/// <inheritdoc cref="IBinaryInteger{TSelf}.DivRem(TSelf, TSelf)" />
public static (char Quotient, char Remainder) DivRem(char left, char right) => ((char, char))Math.DivRem(left, right);
static (char Quotient, char Remainder) IBinaryInteger<char>.DivRem(char left, char right) => ((char, char))Math.DivRem(left, right);

/// <inheritdoc cref="IBinaryInteger{TSelf}.LeadingZeroCount(TSelf)" />
public static char LeadingZeroCount(char value) => (char)(BitOperations.LeadingZeroCount(value) - 16);
static char IBinaryInteger<char>.LeadingZeroCount(char value) => (char)(BitOperations.LeadingZeroCount(value) - 16);

/// <inheritdoc cref="IBinaryInteger{TSelf}.PopCount(TSelf)" />
public static char PopCount(char value) => (char)BitOperations.PopCount(value);
static char IBinaryInteger<char>.PopCount(char value) => (char)BitOperations.PopCount(value);

/// <inheritdoc cref="IBinaryInteger{TSelf}.RotateLeft(TSelf, int)" />
public static char RotateLeft(char value, int rotateAmount) => (char)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15)));
static char IBinaryInteger<char>.RotateLeft(char value, int rotateAmount) => (char)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15)));

/// <inheritdoc cref="IBinaryInteger{TSelf}.RotateRight(TSelf, int)" />
public static char RotateRight(char value, int rotateAmount) => (char)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15)));
static char IBinaryInteger<char>.RotateRight(char value, int rotateAmount) => (char)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15)));

/// <inheritdoc cref="IBinaryInteger{TSelf}.TrailingZeroCount(TSelf)" />
public static char TrailingZeroCount(char value) => (char)(BitOperations.TrailingZeroCount(value << 16) - 16);
static char IBinaryInteger<char>.TrailingZeroCount(char value) => (char)(BitOperations.TrailingZeroCount(value << 16) - 16);

/// <inheritdoc cref="IBinaryInteger{TSelf}.GetShortestBitLength()" />
long IBinaryInteger<char>.GetShortestBitLength() => (sizeof(char) * 8) - LeadingZeroCount(m_value);
int IBinaryInteger<char>.GetShortestBitLength() => (sizeof(char) * 8) - ushort.LeadingZeroCount(m_value);

/// <inheritdoc cref="IBinaryInteger{TSelf}.GetByteCount()" />
int IBinaryInteger<char>.GetByteCount() => sizeof(char);

/// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteBigEndian(Span{byte}, out int)" />
bool IBinaryInteger<char>.TryWriteBigEndian(Span<byte> destination, out int bytesWritten)
{
if (destination.Length >= sizeof(char))
{
ushort value = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(m_value) : m_value;
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value);

bytesWritten = sizeof(char);
return true;
}
else
{
bytesWritten = 0;
return false;
}
}

/// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteLittleEndian(Span{byte}, out int)" />
bool IBinaryInteger<char>.TryWriteLittleEndian(Span<byte> destination, out int bytesWritten)
{
Expand All @@ -1212,10 +1230,10 @@ bool IBinaryInteger<char>.TryWriteLittleEndian(Span<byte> destination, out int b
//

/// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" />
public static bool IsPow2(char value) => BitOperations.IsPow2((uint)value);
static bool IBinaryNumber<char>.IsPow2(char value) => ushort.IsPow2(value);

/// <inheritdoc cref="IBinaryNumber{TSelf}.Log2(TSelf)" />
public static char Log2(char value) => (char)BitOperations.Log2(value);
static char IBinaryNumber<char>.Log2(char value) => (char)(ushort.Log2(value));

//
// IBitwiseOperators
Expand Down Expand Up @@ -1331,15 +1349,14 @@ bool IBinaryInteger<char>.TryWriteLittleEndian(Span<byte> destination, out int b
static char INumber<char>.Abs(char value) => value;

/// <inheritdoc cref="INumber{TSelf}.Clamp(TSelf, TSelf, TSelf)" />
public static char Clamp(char value, char min, char max) => (char)Math.Clamp(value, min, max);
static char INumber<char>.Clamp(char value, char min, char max) => (char)Math.Clamp(value, min, max);

/// <inheritdoc cref="INumber{TSelf}.CopySign(TSelf, TSelf)" />
static char INumber<char>.CopySign(char value, char sign) => value;

/// <inheritdoc cref="INumber{TSelf}.CreateChecked{TOther}(TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char CreateChecked<TOther>(TOther value)
where TOther : INumber<TOther>
static char INumber<char>.CreateChecked<TOther>(TOther value)
{
if (typeof(TOther) == typeof(byte))
{
Expand Down Expand Up @@ -1406,8 +1423,7 @@ public static char CreateChecked<TOther>(TOther value)

/// <inheritdoc cref="INumber{TSelf}.CreateSaturating{TOther}(TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char CreateSaturating<TOther>(TOther value)
where TOther : INumber<TOther>
static char INumber<char>.CreateSaturating<TOther>(TOther value)
{
if (typeof(TOther) == typeof(byte))
{
Expand Down Expand Up @@ -1491,8 +1507,7 @@ public static char CreateSaturating<TOther>(TOther value)

/// <inheritdoc cref="INumber{TSelf}.CreateTruncating{TOther}(TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char CreateTruncating<TOther>(TOther value)
where TOther : INumber<TOther>
static char INumber<char>.CreateTruncating<TOther>(TOther value)
{
if (typeof(TOther) == typeof(byte))
{
Expand Down Expand Up @@ -1561,24 +1576,23 @@ public static char CreateTruncating<TOther>(TOther value)
static bool INumber<char>.IsNegative(char value) => false;

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

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

/// <inheritdoc cref="INumber{TSelf}.Min(TSelf, TSelf)" />
public static char Min(char x, char y) => (char)Math.Min(x, y);
static char INumber<char>.Min(char x, char y) => (char)Math.Min(x, y);

/// <inheritdoc cref="INumber{TSelf}.MinMagnitude(TSelf, TSelf)" />
static char INumber<char>.MinMagnitude(char x, char y) => Min(x, y);
static char INumber<char>.MinMagnitude(char x, char y) => (char)Math.Min(x, y);

/// <inheritdoc cref="INumber{TSelf}.Sign(TSelf)" />
public static int Sign(char value) => (value == 0) ? 0 : 1;
static int INumber<char>.Sign(char value) => (value == 0) ? 0 : 1;

/// <inheritdoc cref="INumber{TSelf}.TryCreate{TOther}(TOther, out TSelf)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryCreate<TOther>(TOther value, out char result)
where TOther : INumber<TOther>
static bool INumber<char>.TryCreate<TOther>(TOther value, out char result)
{
if (typeof(TOther) == typeof(byte))
{
Expand Down
65 changes: 56 additions & 9 deletions src/libraries/System.Private.CoreLib/src/System/Decimal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1150,15 +1150,39 @@ object IConvertible.ToType(Type type, IFormatProvider? provider)
// IFloatingPoint
//

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentByteCount()" />
int IFloatingPoint<decimal>.GetExponentByteCount() => sizeof(sbyte);

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentShortestBitLength()" />
long IFloatingPoint<decimal>.GetExponentShortestBitLength()
int IFloatingPoint<decimal>.GetExponentShortestBitLength()
{
sbyte exponent = Exponent;
return (sizeof(sbyte) * 8) - sbyte.LeadingZeroCount(exponent);
}

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentByteCount()" />
int IFloatingPoint<decimal>.GetExponentByteCount() => sizeof(sbyte);
/// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandByteCount()" />
int IFloatingPoint<decimal>.GetSignificandByteCount() => sizeof(ulong) + sizeof(uint);

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandBitLength()" />
int IFloatingPoint<decimal>.GetSignificandBitLength() => 96;

/// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteExponentBigEndian(Span{byte}, out int)" />
bool IFloatingPoint<decimal>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten)
{
if (destination.Length >= sizeof(sbyte))
{
sbyte exponent = Exponent;
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), exponent);

bytesWritten = sizeof(sbyte);
return true;
}
else
{
bytesWritten = 0;
return false;
}
}

/// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteExponentLittleEndian(Span{byte}, out int)" />
bool IFloatingPoint<decimal>.TryWriteExponentLittleEndian(Span<byte> destination, out int bytesWritten)
Expand All @@ -1178,19 +1202,42 @@ bool IFloatingPoint<decimal>.TryWriteExponentLittleEndian(Span<byte> destination
}
}

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandBitLength()" />
long IFloatingPoint<decimal>.GetSignificandBitLength() => 96;
/// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteSignificandBigEndian(Span{byte}, out int)" />
bool IFloatingPoint<decimal>.TryWriteSignificandBigEndian(Span<byte> destination, out int bytesWritten)
{
if (destination.Length >= (sizeof(uint) + sizeof(ulong)))
{
uint hi32 = _hi32;
ulong lo64 = _lo64;

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandByteCount()" />
int IFloatingPoint<decimal>.GetSignificandByteCount() => sizeof(ulong) + sizeof(uint);
if (BitConverter.IsLittleEndian)
{
hi32 = BinaryPrimitives.ReverseEndianness(hi32);
lo64 = BinaryPrimitives.ReverseEndianness(lo64);
}

ref byte address = ref MemoryMarshal.GetReference(destination);

Unsafe.WriteUnaligned(ref address, hi32);
Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref address, sizeof(uint)), lo64);

bytesWritten = sizeof(uint) + sizeof(ulong);
return true;
}
else
{
bytesWritten = 0;
return false;
}
}

/// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteSignificandLittleEndian(Span{byte}, out int)" />
bool IFloatingPoint<decimal>.TryWriteSignificandLittleEndian(Span<byte> destination, out int bytesWritten)
{
if (destination.Length >= (sizeof(ulong) + sizeof(uint)))
{
var lo64 = _lo64;
var hi32 = _hi32;
ulong lo64 = _lo64;
uint hi32 = _hi32;

if (!BitConverter.IsLittleEndian)
{
Expand Down
62 changes: 55 additions & 7 deletions src/libraries/System.Private.CoreLib/src/System/Double.cs
Original file line number Diff line number Diff line change
Expand Up @@ -661,8 +661,11 @@ public static bool IsPow2(double value)
/// <inheritdoc cref="IFloatingPoint{TSelf}.Truncate(TSelf)" />
public static double Truncate(double x) => Math.Truncate(x);

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentByteCount()" />
int IFloatingPoint<double>.GetExponentByteCount() => sizeof(short);

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentShortestBitLength()" />
long IFloatingPoint<double>.GetExponentShortestBitLength()
int IFloatingPoint<double>.GetExponentShortestBitLength()
{
short exponent = Exponent;

Expand All @@ -676,8 +679,35 @@ long IFloatingPoint<double>.GetExponentShortestBitLength()
}
}

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentByteCount()" />
int IFloatingPoint<double>.GetExponentByteCount() => sizeof(short);
/// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandByteCount()" />
int IFloatingPoint<double>.GetSignificandByteCount() => sizeof(ulong);

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandBitLength()" />
int IFloatingPoint<double>.GetSignificandBitLength() => 53;

/// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteExponentBigEndian(Span{byte}, out int)" />
bool IFloatingPoint<double>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten)
{
if (destination.Length >= sizeof(short))
{
short exponent = Exponent;

if (BitConverter.IsLittleEndian)
{
exponent = BinaryPrimitives.ReverseEndianness(exponent);
}

Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), exponent);

bytesWritten = sizeof(short);
return true;
}
else
{
bytesWritten = 0;
return false;
}
}

/// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteExponentLittleEndian(Span{byte}, out int)" />
bool IFloatingPoint<double>.TryWriteExponentLittleEndian(Span<byte> destination, out int bytesWritten)
Expand All @@ -703,11 +733,29 @@ bool IFloatingPoint<double>.TryWriteExponentLittleEndian(Span<byte> destination,
}
}

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandBitLength()" />
long IFloatingPoint<double>.GetSignificandBitLength() => 53;
/// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteSignificandBigEndian(Span{byte}, out int)" />
bool IFloatingPoint<double>.TryWriteSignificandBigEndian(Span<byte> destination, out int bytesWritten)
{
if (destination.Length >= sizeof(ulong))
{
ulong significand = Significand;

/// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandByteCount()" />
int IFloatingPoint<double>.GetSignificandByteCount() => sizeof(ulong);
if (BitConverter.IsLittleEndian)
{
significand = BinaryPrimitives.ReverseEndianness(significand);
}

Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), significand);

bytesWritten = sizeof(ulong);
return true;
}
else
{
bytesWritten = 0;
return false;
}
}

/// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteSignificandLittleEndian(Span{byte}, out int)" />
bool IFloatingPoint<double>.TryWriteSignificandLittleEndian(Span<byte> destination, out int bytesWritten)
Expand Down
Loading