From 970e636dc33b6333e73fe0667ffd0197555ace5f Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 22 Nov 2020 21:29:12 +0300 Subject: [PATCH 1/8] Add Tuple-based Math.DivRem overloads --- .../System.Private.CoreLib/src/System/Math.cs | 65 ++++ .../tests/System/Math.cs | 291 ++++++++++++++++-- .../System.Runtime/ref/System.Runtime.cs | 15 + 3 files changed, 347 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index f1c058281739a..4129746ad4c6a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -285,6 +285,71 @@ internal static ulong DivRem(ulong a, ulong b, out ulong result) return div; } + [CLSCompliant(false)] + public static (sbyte Quotient, sbyte Remainder) DivRem(sbyte left, sbyte right) + { + sbyte quotient = (sbyte)(left / right); + return (quotient, (sbyte)(left - (quotient * right))); + } + + public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) + { + byte quotient = (byte)(left / right); + return (quotient, (byte)(left - (quotient * right))); + } + + public static (short Quotient, short Remainder) DivRem(short left, short right) + { + short quotient = (short)(left / right); + return (quotient, (short)(left - (quotient * right))); + } + + [CLSCompliant(false)] + public static (ushort Quotient, ushort Remainder) DivRem(ushort left, ushort right) + { + ushort quotient = (ushort)(left / right); + return (quotient, (ushort)(left - (quotient * right))); + } + + public static (int Quotient, int Remainder) DivRem(int left, int right) + { + int quotient = left / right; + return (quotient, left - (quotient * right)); + } + + [CLSCompliant(false)] + public static (uint Quotient, uint Remainder) DivRem(uint left, uint right) + { + uint quotient = left / right; + return (quotient, left - (quotient * right)); + } + + public static (long Quotient, long Remainder) DivRem(long left, long right) + { + long quotient = left / right; + return (quotient, left - (quotient * right)); + } + + [CLSCompliant(false)] + public static (ulong Quotient, ulong Remainder) DivRem(ulong left, ulong right) + { + ulong quotient = left / right; + return (quotient, left - (quotient * right)); + } + + public static (nint Quotient, nint Remainder) DivRem(nint left, nint right) + { + nint quotient = left / right; + return (quotient, left - (quotient * right)); + } + + [CLSCompliant(false)] + public static (nuint Quotient, nuint Remainder) DivRem(nuint left, nuint right) + { + nuint quotient = left / right; + return (quotient, left - (quotient * right)); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal Ceiling(decimal d) { diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Math.cs b/src/libraries/System.Runtime.Extensions/tests/System/Math.cs index dde176dce8a6c..a774266567212 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Math.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Math.cs @@ -4,6 +4,7 @@ using Xunit; using Xunit.Sdk; using System.Collections.Generic; +using System.Runtime.CompilerServices; #pragma warning disable xUnit1025 // reporting duplicate test cases due to not distinguishing 0.0 from -0.0 @@ -1802,34 +1803,276 @@ public static void BigMul128_Signed(long a, long b, string result) } [Theory] - [InlineData(1073741, 2147483647, 2000, 1647)] - [InlineData(6, 13952, 2000, 1952)] - [InlineData(0, 0, 2000, 0)] - [InlineData(-7, -14032, 2000, -32)] - [InlineData(-1073741, -2147483648, 2000, -1648)] - [InlineData(-1073741, 2147483647, -2000, 1647)] - [InlineData(-6, 13952, -2000, 1952)] - public static void DivRem(int quotient, int dividend, int divisor, int expectedRemainder) - { - int remainder; - Assert.Equal(quotient, Math.DivRem(dividend, divisor, out remainder)); - Assert.Equal(expectedRemainder, remainder); + [InlineData(sbyte.MaxValue, sbyte.MaxValue, 1, 0)] + [InlineData(sbyte.MaxValue, 1, sbyte.MaxValue, 0)] + [InlineData(sbyte.MaxValue, 2, 63, 1)] + [InlineData(sbyte.MaxValue, -1, -127, 0)] + [InlineData(11, 22, 0, 11)] + [InlineData(80, 22, 3, 14)] + [InlineData(80, -22, -3, 14)] + [InlineData(-80, 22, -3, -14)] + [InlineData(-80, -22, 3, -14)] + [InlineData(0, 1, 0, 0)] + [InlineData(0, sbyte.MaxValue, 0, 0)] + [InlineData(sbyte.MinValue, sbyte.MaxValue, -1, -1)] + [InlineData(sbyte.MaxValue, 0, 0, 0)] + [InlineData(1, 0, 0, 0)] + [InlineData(0, 0, 0, 0)] + public static void DivRemSByte(sbyte dividend, sbyte divisor, sbyte expectedQuotient, sbyte expectedRemainder) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + } + + [Theory] + [InlineData(byte.MaxValue, byte.MaxValue, 1, 0)] + [InlineData(byte.MaxValue, 1, byte.MaxValue, 0)] + [InlineData(byte.MaxValue, 2, 127, 1)] + [InlineData(52, 5, 10, 2)] + [InlineData(100, 33, 3, 1)] + [InlineData(0, 1, 0, 0)] + [InlineData(0, byte.MaxValue, 0, 0)] + [InlineData(250, 50, 5, 0)] + [InlineData(byte.MaxValue, 0, 0, 0)] + [InlineData(1, 0, 0, 0)] + [InlineData(0, 0, 0, 0)] + public static void DivRemByte(byte dividend, byte divisor, byte expectedQuotient, byte expectedRemainder) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + } + + [Theory] + [InlineData(short.MaxValue, short.MaxValue, 1, 0)] + [InlineData(short.MaxValue, 1, short.MaxValue, 0)] + [InlineData(short.MaxValue, 2, 16383, 1)] + [InlineData(short.MaxValue, -1, -32767, 0)] + [InlineData(12345, 22424, 0, 12345)] + [InlineData(300, 22, 13, 14)] + [InlineData(300, -22, -13, 14)] + [InlineData(-300, 22, -13, -14)] + [InlineData(-300, -22, 13, -14)] + [InlineData(0, 1, 0, 0)] + [InlineData(0, short.MaxValue, 0, 0)] + [InlineData(short.MinValue, short.MaxValue, -1, -1)] + [InlineData(13952, 2000, 6, 1952)] + [InlineData(short.MaxValue, 0, 0, 0)] + [InlineData(1, 0, 0, 0)] + [InlineData(0, 0, 0, 0)] + public static void DivRemInt16(short dividend, short divisor, short expectedQuotient, short expectedRemainder) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + } + + [Theory] + [InlineData(ushort.MaxValue, ushort.MaxValue, 1, 0)] + [InlineData(ushort.MaxValue, 1, ushort.MaxValue, 0)] + [InlineData(ushort.MaxValue, 2, 32767, 1)] + [InlineData(12345, 42424, 0, 12345)] + [InlineData(51474, 31474, 1, 20000)] + [InlineData(10000, 333, 30, 10)] + [InlineData(0, 1, 0, 0)] + [InlineData(0, ushort.MaxValue, 0, 0)] + [InlineData(13952, 2000, 6, 1952)] + [InlineData(ushort.MaxValue, 0, 0, 0)] + [InlineData(1, 0, 0, 0)] + [InlineData(0, 0, 0, 0)] + public static void DivRemUInt16(ushort dividend, ushort divisor, ushort expectedQuotient, ushort expectedRemainder) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + } + + [Theory] + [InlineData(2147483647, 2000, 1073741, 1647)] + [InlineData(13952, 2000, 6, 1952)] + [InlineData(0, 2000, 0, 0)] + [InlineData(-14032, 2000, -7, -32)] + [InlineData(-2147483648, 2000, -1073741, -1648)] + [InlineData(2147483647, -2000, -1073741, 1647)] + [InlineData(13952, -2000, -6, 1952)] + [InlineData(13952, 0, 0, 0)] + [InlineData(int.MaxValue, 0, 0, 0)] + [InlineData(0, 0, 0, 0)] + public static void DivRemInt32(int dividend, int divisor, int expectedQuotient, int expectedRemainder) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + Assert.Throws(() => Math.DivRem(dividend, divisor, out int remainder)); + } + else + { + Assert.Equal(expectedQuotient, Math.DivRem(dividend, divisor, out int remainder)); + Assert.Equal(expectedRemainder, remainder); + + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + if (IntPtr.Size == 4) + { + DivRemNativeInt(dividend, divisor, expectedQuotient, expectedRemainder); + } + } + + [Theory] + [InlineData(uint.MaxValue, uint.MaxValue, 1, 0)] + [InlineData(uint.MaxValue, 1, uint.MaxValue, 0)] + [InlineData(uint.MaxValue, 2, 2147483647, 1)] + [InlineData(123456789, 4242424242, 0, 123456789)] + [InlineData(514748364, 3147483647, 0, 514748364)] + [InlineData(1000000, 333, 3003, 1)] + [InlineData(0, 1, 0, 0)] + [InlineData(0UL, uint.MaxValue, 0, 0)] + [InlineData(13952, 2000, 6, 1952)] + [InlineData(uint.MaxValue, 0, 0, 0)] + [InlineData(1, 0, 0, 0)] + [InlineData(0, 0, 0, 0)] + public static void DivRemUInt32(uint dividend, uint divisor, uint expectedQuotient, uint expectedRemainder) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + if (IntPtr.Size == 4) + { + DivRemNativeUInt(dividend, divisor, expectedQuotient, expectedRemainder); + } } [Theory] - [InlineData(4611686018427387L, 9223372036854775807L, 2000L, 1807L)] - [InlineData(4611686018427387L, -9223372036854775808L, -2000L, -1808L)] - [InlineData(-4611686018427387L, 9223372036854775807L, -2000L, 1807L)] - [InlineData(-4611686018427387L, -9223372036854775808L, 2000L, -1808L)] - [InlineData(6L, 13952L, 2000L, 1952L)] - [InlineData(0L, 0L, 2000L, 0L)] - [InlineData(-7L, -14032L, 2000L, -32L)] - [InlineData(-6L, 13952L, -2000L, 1952L)] - public static void DivRemLong(long quotient, long dividend, long divisor, long expectedRemainder) + [InlineData(9223372036854775807L, 2000L, 4611686018427387L, 1807L)] + [InlineData(-9223372036854775808L, -2000L, 4611686018427387L, -1808L)] + [InlineData(9223372036854775807L, -2000L, -4611686018427387L, 1807L)] + [InlineData(-9223372036854775808L, 2000L, -4611686018427387L, -1808L)] + [InlineData(13952L, 2000L, 6L, 1952L)] + [InlineData(0L, 2000L, 0L, 0L)] + [InlineData(-14032L, 2000L, -7L, -32L)] + [InlineData(13952L, -2000L, -6L, 1952L)] + [InlineData(long.MaxValue, 0, 0, 0)] + [InlineData(1, 0, 0, 0)] + [InlineData(0, 0, 0, 0)] + public static void DivRemInt64(long dividend, long divisor, long expectedQuotient, long expectedRemainder) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + Assert.Throws(() => Math.DivRem(dividend, divisor, out long remainder)); + } + else + { + Assert.Equal(expectedQuotient, Math.DivRem(dividend, divisor, out long remainder)); + Assert.Equal(expectedRemainder, remainder); + + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + if (IntPtr.Size == 8) + { + DivRemNativeInt((nint)dividend, (nint)divisor, (nint)expectedQuotient, (nint)expectedRemainder); + } + } + + [Theory] + [InlineData(ulong.MaxValue, ulong.MaxValue, 1, 0)] + [InlineData(ulong.MaxValue, 1, ulong.MaxValue, 0)] + [InlineData(ulong.MaxValue, 2, 9223372036854775807, 1)] + [InlineData(123456789, 4242424242, 0, 123456789)] + [InlineData(5147483647, 3147483647, 1, 2000000000)] + [InlineData(1000000, 333, 3003, 1)] + [InlineData(0, 1, 0, 0)] + [InlineData(0UL, ulong.MaxValue, 0, 0)] + [InlineData(13952, 2000, 6, 1952)] + [InlineData(ulong.MaxValue, 0, 0, 0)] + [InlineData(1, 0, 0, 0)] + [InlineData(0, 0, 0, 0)] + public static void DivRemUInt64(ulong dividend, ulong divisor, ulong expectedQuotient, ulong expectedRemainder) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + if (IntPtr.Size == 8) + { + DivRemNativeUInt((nuint)dividend, (nuint)divisor, (nuint)expectedQuotient, (nuint)expectedRemainder); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void DivRemNativeInt(nint dividend, nint divisor, nint expectedQuotient, nint expectedRemainder) { - long remainder; - Assert.Equal(quotient, Math.DivRem(dividend, divisor, out remainder)); - Assert.Equal(expectedRemainder, remainder); + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void DivRemNativeUInt(nuint dividend, nuint divisor, nuint expectedQuotient, nuint expectedRemainder) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } } public static IEnumerable Clamp_UnsignedInt_TestData() diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 55d0809834830..d92bf693dad74 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -2696,6 +2696,21 @@ public static partial class Math public static double Cosh(double value) { throw null; } public static int DivRem(int a, int b, out int result) { throw null; } public static long DivRem(long a, long b, out long result) { throw null; } + public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) { throw null; } + [System.CLSCompliantAttribute(false)] + public static (sbyte Quotient, sbyte Remainder) DivRem(sbyte left, sbyte right) { throw null; } + public static (short Quotient, short Remainder) DivRem(short left, short right) { throw null; } + [System.CLSCompliantAttribute(false)] + public static (ushort Quotient, ushort Remainder) DivRem(ushort left, ushort right) { throw null; } + public static (int Quotient, int Remainder) DivRem(int left, int right) { throw null; } + [System.CLSCompliantAttribute(false)] + public static (uint Quotient, uint Remainder) DivRem(uint left, uint right) { throw null; } + public static (long Quotient, long Remainder) DivRem(long left, long right) { throw null; } + [System.CLSCompliantAttribute(false)] + public static (ulong Quotient, ulong Remainder) DivRem(ulong left, ulong right) { throw null; } + public static (nint Quotient, nint Remainder) DivRem(nint left, nint right) { throw null; } + [System.CLSCompliantAttribute(false)] + public static (nuint Quotient, nuint Remainder) DivRem(nuint left, nuint right) { throw null; } public static double Exp(double d) { throw null; } public static decimal Floor(decimal d) { throw null; } public static double Floor(double d) { throw null; } From 41f558a19f41e5dcdbf87203c62feb90fb85b453 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Mon, 23 Nov 2020 15:40:41 +0300 Subject: [PATCH 2/8] Remove internal versions of Math.DivRem --- .../src/System/Net/IPAddressParser.cs | 16 +++++++--------- .../src/System/Buffers/StandardFormat.cs | 3 ++- .../src/System/Globalization/TimeSpanFormat.cs | 9 +++++---- .../System.Private.CoreLib/src/System/Math.cs | 14 -------------- .../src/System/Number.BigInteger.cs | 5 +++-- .../src/System/Number.Formatting.cs | 12 ++++++++---- .../src/System/Number.Grisu3.cs | 6 ++++-- .../System/Number.NumberToFloatingPointBits.cs | 2 +- 8 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs b/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs index fede4c415feb5..007c266951566 100644 --- a/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs +++ b/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs @@ -78,13 +78,13 @@ private static unsafe int IPv4AddressToStringHelper(uint address, char* addressS { int offset = 0; - FormatIPv4AddressNumber((int)(address & 0xFF), addressString, ref offset); + FormatIPv4AddressNumber((byte)address, addressString, ref offset); addressString[offset++] = '.'; - FormatIPv4AddressNumber((int)((address >> 8) & 0xFF), addressString, ref offset); + FormatIPv4AddressNumber((byte)(address >> 8), addressString, ref offset); addressString[offset++] = '.'; - FormatIPv4AddressNumber((int)((address >> 16) & 0xFF), addressString, ref offset); + FormatIPv4AddressNumber((byte)(address >> 16), addressString, ref offset); addressString[offset++] = '.'; - FormatIPv4AddressNumber((int)((address >> 24) & 0xFF), addressString, ref offset); + FormatIPv4AddressNumber((byte)(address >> 24), addressString, ref offset); return offset; } @@ -153,17 +153,15 @@ internal static StringBuilder IPv6AddressToStringHelper(ushort[] address, uint s return buffer; } - private static unsafe void FormatIPv4AddressNumber(int number, char* addressString, ref int offset) + private static unsafe void FormatIPv4AddressNumber(byte number, char* addressString, ref int offset) { - // Math.DivRem has no overload for byte, assert here for safety - Debug.Assert(number < 256); - offset += number > 99 ? 3 : number > 9 ? 2 : 1; int i = offset; do { - number = Math.DivRem(number, 10, out int rem); + byte rem; + (number, rem) = Math.DivRem(number, 10); addressString[--i] = (char)('0' + rem); } while (number != 0); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs index 1aeb2ff7e6d08..46f0847405341 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs @@ -182,7 +182,8 @@ internal int Format(Span destination) if (precision >= 10) { - uint div = Math.DivRem(precision, 10, out precision); + uint div; + (div, precision) = Math.DivRem(precision, 10); destination[1] = (char)('0' + div % 10); count = 2; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs index c82a2c3c950fc..1ed63ae31a7b5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs @@ -129,7 +129,8 @@ private static bool TryFormatStandard(TimeSpan value, StandardFormat format, str } } - totalSecondsRemaining = Math.DivRem((ulong)ticks, TimeSpan.TicksPerSecond, out ulong fraction64); + ulong fraction64; + (totalSecondsRemaining, fraction64) = Math.DivRem((ulong)ticks, TimeSpan.TicksPerSecond); fraction = (uint)fraction64; } @@ -170,7 +171,7 @@ private static bool TryFormatStandard(TimeSpan value, StandardFormat format, str if (totalSecondsRemaining > 0) { // Only compute minutes if the TimeSpan has an absolute value of >= 1 minute. - totalMinutesRemaining = Math.DivRem(totalSecondsRemaining, 60 /* seconds per minute */, out seconds); + (totalMinutesRemaining, seconds) = Math.DivRem(totalSecondsRemaining, 60 /* seconds per minute */); Debug.Assert(seconds < 60); } @@ -178,7 +179,7 @@ private static bool TryFormatStandard(TimeSpan value, StandardFormat format, str if (totalMinutesRemaining > 0) { // Only compute hours if the TimeSpan has an absolute value of >= 1 hour. - totalHoursRemaining = Math.DivRem(totalMinutesRemaining, 60 /* minutes per hour */, out minutes); + (totalHoursRemaining, minutes) = Math.DivRem(totalMinutesRemaining, 60 /* minutes per hour */); Debug.Assert(minutes < 60); } @@ -189,7 +190,7 @@ private static bool TryFormatStandard(TimeSpan value, StandardFormat format, str if (totalHoursRemaining > 0) { // Only compute days if the TimeSpan has an absolute value of >= 1 day. - days = Math.DivRem((uint)totalHoursRemaining, 24 /* hours per day */, out hours); + (days, hours) = Math.DivRem((uint)totalHoursRemaining, 24 /* hours per day */); Debug.Assert(hours < 24); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 4129746ad4c6a..06035caf8e522 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -271,20 +271,6 @@ public static long DivRem(long a, long b, out long result) return div; } - internal static uint DivRem(uint a, uint b, out uint result) - { - uint div = a / b; - result = a - (div * b); - return div; - } - - internal static ulong DivRem(ulong a, ulong b, out ulong result) - { - ulong div = a / b; - result = a - (div * b); - return div; - } - [CLSCompliant(false)] public static (sbyte Quotient, sbyte Remainder) DivRem(sbyte left, sbyte right) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs b/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs index 329784737d316..2888d7e73a038 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs @@ -446,7 +446,7 @@ public static void DivRem(ref BigInteger lhs, ref BigInteger rhs, out BigInteger if ((lhsLength == 1) && (rhsLength == 1)) { - uint quotient = Math.DivRem(lhs._blocks[0], rhs._blocks[0], out uint remainder); + var (quotient, remainder) = Math.DivRem(lhs._blocks[0], rhs._blocks[0]); SetUInt32(out quo, quotient); SetUInt32(out rem, remainder); return; @@ -464,7 +464,8 @@ public static void DivRem(ref BigInteger lhs, ref BigInteger rhs, out BigInteger for (int i = quoLength - 1; i >= 0; i--) { ulong value = (carry << 32) | lhs._blocks[i]; - ulong digit = Math.DivRem(value, rhsValue, out carry); + ulong digit; + (digit, carry) = Math.DivRem(value, rhsValue); if ((digit == 0) && (i == (quoLength - 1))) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index 299e394f509a8..cc05a9b8d4558 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -1334,7 +1334,8 @@ private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number) { while (--digits >= 0 || value != 0) { - value = Math.DivRem(value, 10, out uint remainder); + uint remainder; + (value, remainder) = Math.DivRem(value, 10); *(--bufferEnd) = (byte)(remainder + '0'); } return bufferEnd; @@ -1344,7 +1345,8 @@ private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number) { while (--digits >= 0 || value != 0) { - value = Math.DivRem(value, 10, out uint remainder); + uint remainder; + (value, remainder) = Math.DivRem(value, 10); *(--bufferEnd) = (char)(remainder + '0'); } return bufferEnd; @@ -1367,7 +1369,8 @@ internal static unsafe string UInt32ToDecStr(uint value) char* p = buffer + bufferLength; do { - value = Math.DivRem(value, 10, out uint remainder); + uint remainder; + (value, remainder) = Math.DivRem(value, 10); *(--p) = (char)(remainder + '0'); } while (value != 0); @@ -1409,7 +1412,8 @@ private static unsafe bool TryUInt32ToDecStr(uint value, int digits, Span { do { - value = Math.DivRem(value, 10, out uint remainder); + uint remainder; + (value, remainder) = Math.DivRem(value, 10); *(--p) = (char)(remainder + '0'); } while (value != 0); diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs index 474f7074a217a..b1217121ef6b5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs @@ -624,7 +624,8 @@ private static bool TryDigitGenCounted(in DiyFp w, int requestedDigits, Span 0) { - uint digit = Math.DivRem(integrals, divisor, out integrals); + uint digit; + (digit, integrals) = Math.DivRem(integrals, divisor); Debug.Assert(digit <= 9); buffer[length] = (byte)('0' + digit); @@ -802,7 +803,8 @@ private static bool TryDigitGenShortest(in DiyFp low, in DiyFp w, in DiyFp high, // The divisor is the biggest power of ten that is smaller than integrals while (kappa > 0) { - uint digit = Math.DivRem(integrals, divisor, out integrals); + uint digit; + (digit, integrals) = Math.DivRem(integrals, divisor); Debug.Assert(digit <= 9); buffer[length] = (byte)('0' + digit); diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs b/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs index 788697ffb70a9..db94c39c92be7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs @@ -261,7 +261,7 @@ private static ulong ConvertBigIntegerToFloatingPointBits(ref BigInteger value, return AssembleFloatingPointBits(in info, value.ToUInt64(), baseExponent, !hasNonZeroFractionalPart); } - uint topBlockIndex = Math.DivRem(integerBitsOfPrecision, 32, out uint topBlockBits); + var (topBlockIndex, topBlockBits) = Math.DivRem(integerBitsOfPrecision, 32); uint middleBlockIndex = topBlockIndex - 1; uint bottomBlockIndex = middleBlockIndex - 1; From 3a0159b05f2cdbe6d59296511f46cc17c931f965 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Mon, 23 Nov 2020 15:51:13 +0300 Subject: [PATCH 3/8] fix build --- .../System.Net.Primitives/src/System/Net/IPAddressParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs b/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs index 007c266951566..296a9500ad489 100644 --- a/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs +++ b/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs @@ -161,7 +161,7 @@ private static unsafe void FormatIPv4AddressNumber(byte number, char* addressStr do { byte rem; - (number, rem) = Math.DivRem(number, 10); + (number, rem) = Math.DivRem(number, (byte)10); addressString[--i] = (char)('0' + rem); } while (number != 0); } From 60a8d989a8222d144b298b30ae60cc507427e119 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Mon, 23 Nov 2020 18:21:49 +0300 Subject: [PATCH 4/8] Update IPAddressParser.cs --- .../src/System/Net/IPAddressParser.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs b/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs index 296a9500ad489..fede4c415feb5 100644 --- a/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs +++ b/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs @@ -78,13 +78,13 @@ private static unsafe int IPv4AddressToStringHelper(uint address, char* addressS { int offset = 0; - FormatIPv4AddressNumber((byte)address, addressString, ref offset); + FormatIPv4AddressNumber((int)(address & 0xFF), addressString, ref offset); addressString[offset++] = '.'; - FormatIPv4AddressNumber((byte)(address >> 8), addressString, ref offset); + FormatIPv4AddressNumber((int)((address >> 8) & 0xFF), addressString, ref offset); addressString[offset++] = '.'; - FormatIPv4AddressNumber((byte)(address >> 16), addressString, ref offset); + FormatIPv4AddressNumber((int)((address >> 16) & 0xFF), addressString, ref offset); addressString[offset++] = '.'; - FormatIPv4AddressNumber((byte)(address >> 24), addressString, ref offset); + FormatIPv4AddressNumber((int)((address >> 24) & 0xFF), addressString, ref offset); return offset; } @@ -153,15 +153,17 @@ internal static StringBuilder IPv6AddressToStringHelper(ushort[] address, uint s return buffer; } - private static unsafe void FormatIPv4AddressNumber(byte number, char* addressString, ref int offset) + private static unsafe void FormatIPv4AddressNumber(int number, char* addressString, ref int offset) { + // Math.DivRem has no overload for byte, assert here for safety + Debug.Assert(number < 256); + offset += number > 99 ? 3 : number > 9 ? 2 : 1; int i = offset; do { - byte rem; - (number, rem) = Math.DivRem(number, (byte)10); + number = Math.DivRem(number, 10, out int rem); addressString[--i] = (char)('0' + rem); } while (number != 0); } From 32c7cc5239416b780d47cd13030ed596c699f638 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Mon, 23 Nov 2020 18:39:32 +0300 Subject: [PATCH 5/8] Add AggressiveInlining --- .../System.Private.CoreLib/src/System/Math.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 06035caf8e522..8eb162b20d0cf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -272,18 +272,21 @@ public static long DivRem(long a, long b, out long result) } [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (sbyte Quotient, sbyte Remainder) DivRem(sbyte left, sbyte right) { sbyte quotient = (sbyte)(left / right); return (quotient, (sbyte)(left - (quotient * right))); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) { byte quotient = (byte)(left / right); return (quotient, (byte)(left - (quotient * right))); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (short Quotient, short Remainder) DivRem(short left, short right) { short quotient = (short)(left / right); @@ -291,12 +294,14 @@ public static (short Quotient, short Remainder) DivRem(short left, short right) } [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (ushort Quotient, ushort Remainder) DivRem(ushort left, ushort right) { ushort quotient = (ushort)(left / right); return (quotient, (ushort)(left - (quotient * right))); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (int Quotient, int Remainder) DivRem(int left, int right) { int quotient = left / right; @@ -304,12 +309,14 @@ public static (int Quotient, int Remainder) DivRem(int left, int right) } [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (uint Quotient, uint Remainder) DivRem(uint left, uint right) { uint quotient = left / right; return (quotient, left - (quotient * right)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (long Quotient, long Remainder) DivRem(long left, long right) { long quotient = left / right; @@ -317,12 +324,14 @@ public static (long Quotient, long Remainder) DivRem(long left, long right) } [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (ulong Quotient, ulong Remainder) DivRem(ulong left, ulong right) { ulong quotient = left / right; return (quotient, left - (quotient * right)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (nint Quotient, nint Remainder) DivRem(nint left, nint right) { nint quotient = left / right; @@ -330,6 +339,7 @@ public static (nint Quotient, nint Remainder) DivRem(nint left, nint right) } [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (nuint Quotient, nuint Remainder) DivRem(nuint left, nuint right) { nuint quotient = left / right; From e6faee959f7d1178b74d532b3d59c03405af229c Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Mon, 23 Nov 2020 19:05:03 +0300 Subject: [PATCH 6/8] Update Math.cs --- .../System.Private.CoreLib/src/System/Math.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 8eb162b20d0cf..b039d2e1116a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -271,6 +271,7 @@ public static long DivRem(long a, long b, out long result) return div; } + [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (sbyte Quotient, sbyte Remainder) DivRem(sbyte left, sbyte right) @@ -279,6 +280,7 @@ public static (sbyte Quotient, sbyte Remainder) DivRem(sbyte left, sbyte right) return (quotient, (sbyte)(left - (quotient * right))); } + [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) { @@ -286,6 +288,7 @@ public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) return (quotient, (byte)(left - (quotient * right))); } + [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (short Quotient, short Remainder) DivRem(short left, short right) { @@ -293,6 +296,7 @@ public static (short Quotient, short Remainder) DivRem(short left, short right) return (quotient, (short)(left - (quotient * right))); } + [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (ushort Quotient, ushort Remainder) DivRem(ushort left, ushort right) @@ -301,6 +305,7 @@ public static (ushort Quotient, ushort Remainder) DivRem(ushort left, ushort rig return (quotient, (ushort)(left - (quotient * right))); } + [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (int Quotient, int Remainder) DivRem(int left, int right) { @@ -308,6 +313,7 @@ public static (int Quotient, int Remainder) DivRem(int left, int right) return (quotient, left - (quotient * right)); } + [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (uint Quotient, uint Remainder) DivRem(uint left, uint right) @@ -316,6 +322,7 @@ public static (uint Quotient, uint Remainder) DivRem(uint left, uint right) return (quotient, left - (quotient * right)); } + [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (long Quotient, long Remainder) DivRem(long left, long right) { @@ -323,6 +330,7 @@ public static (long Quotient, long Remainder) DivRem(long left, long right) return (quotient, left - (quotient * right)); } + [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (ulong Quotient, ulong Remainder) DivRem(ulong left, ulong right) @@ -331,6 +339,7 @@ public static (ulong Quotient, ulong Remainder) DivRem(ulong left, ulong right) return (quotient, left - (quotient * right)); } + [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (nint Quotient, nint Remainder) DivRem(nint left, nint right) { @@ -338,6 +347,7 @@ public static (nint Quotient, nint Remainder) DivRem(nint left, nint right) return (quotient, left - (quotient * right)); } + [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (nuint Quotient, nuint Remainder) DivRem(nuint left, nuint right) From 8f5c0dd23a9f9a1c8dfa65d524eecb3c9d0f58ff Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 26 Nov 2020 00:43:26 +0300 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Stephen Toub --- .../System.Private.CoreLib/src/System/Number.BigInteger.cs | 2 +- .../src/System/Number.NumberToFloatingPointBits.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs b/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs index 2888d7e73a038..51cfa45736afb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs @@ -446,7 +446,7 @@ public static void DivRem(ref BigInteger lhs, ref BigInteger rhs, out BigInteger if ((lhsLength == 1) && (rhsLength == 1)) { - var (quotient, remainder) = Math.DivRem(lhs._blocks[0], rhs._blocks[0]); + (uint quotient, uint remainder) = Math.DivRem(lhs._blocks[0], rhs._blocks[0]); SetUInt32(out quo, quotient); SetUInt32(out rem, remainder); return; diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs b/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs index db94c39c92be7..6da4045ea67a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs @@ -261,7 +261,7 @@ private static ulong ConvertBigIntegerToFloatingPointBits(ref BigInteger value, return AssembleFloatingPointBits(in info, value.ToUInt64(), baseExponent, !hasNonZeroFractionalPart); } - var (topBlockIndex, topBlockBits) = Math.DivRem(integerBitsOfPrecision, 32); + (uint topBlockIndex, uint topBlockBits) = Math.DivRem(integerBitsOfPrecision, 32); uint middleBlockIndex = topBlockIndex - 1; uint bottomBlockIndex = middleBlockIndex - 1; From 0ba18f5b1e265481c2a15e3a97c6af1487f16c02 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 26 Nov 2020 00:51:47 +0300 Subject: [PATCH 8/8] Add comments --- .../System.Private.CoreLib/src/System/Math.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index b039d2e1116a8..335a3cc2c14a5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -271,6 +271,10 @@ public static long DivRem(long a, long b, out long result) return div; } + /// Produces the quotient and the remainder of two signed 8-bit numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -280,6 +284,10 @@ public static (sbyte Quotient, sbyte Remainder) DivRem(sbyte left, sbyte right) return (quotient, (sbyte)(left - (quotient * right))); } + /// Produces the quotient and the remainder of two unsigned 8-bit numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) @@ -288,6 +296,10 @@ public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) return (quotient, (byte)(left - (quotient * right))); } + /// Produces the quotient and the remainder of two signed 16-bit numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (short Quotient, short Remainder) DivRem(short left, short right) @@ -296,6 +308,10 @@ public static (short Quotient, short Remainder) DivRem(short left, short right) return (quotient, (short)(left - (quotient * right))); } + /// Produces the quotient and the remainder of two unsigned 16-bit numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -305,6 +321,10 @@ public static (ushort Quotient, ushort Remainder) DivRem(ushort left, ushort rig return (quotient, (ushort)(left - (quotient * right))); } + /// Produces the quotient and the remainder of two signed 32-bit numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (int Quotient, int Remainder) DivRem(int left, int right) @@ -313,6 +333,10 @@ public static (int Quotient, int Remainder) DivRem(int left, int right) return (quotient, left - (quotient * right)); } + /// Produces the quotient and the remainder of two unsigned 32-bit numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -322,6 +346,10 @@ public static (uint Quotient, uint Remainder) DivRem(uint left, uint right) return (quotient, left - (quotient * right)); } + /// Produces the quotient and the remainder of two signed 64-bit numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (long Quotient, long Remainder) DivRem(long left, long right) @@ -330,6 +358,10 @@ public static (long Quotient, long Remainder) DivRem(long left, long right) return (quotient, left - (quotient * right)); } + /// Produces the quotient and the remainder of two unsigned 64-bit numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -339,6 +371,10 @@ public static (ulong Quotient, ulong Remainder) DivRem(ulong left, ulong right) return (quotient, left - (quotient * right)); } + /// Produces the quotient and the remainder of two signed native-size numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (nint Quotient, nint Remainder) DivRem(nint left, nint right) @@ -347,6 +383,10 @@ public static (nint Quotient, nint Remainder) DivRem(nint left, nint right) return (quotient, left - (quotient * right)); } + /// Produces the quotient and the remainder of two unsigned native-size numbers. + /// The dividend. + /// The divisor. + /// The quotient and the remainder of the specified numbers. [NonVersionable] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)]