diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 65380d43f3c739..bfc8e8c8bf0e25 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -1180,7 +1180,12 @@ public static Int128 Log2(Int128 value) return lower; } - internal static Int128 BigMul(Int128 left, Int128 right, out Int128 lower) + /// Produces the full product of two unsigned native integers. + /// The integer to multiply with . + /// The integer to multiply with . + /// The lower half of the full product. + /// The upper half of the full product. + public static Int128 BigMul(Int128 left, Int128 right, out Int128 lower) { // This follows the same logic as is used in `long Math.BigMul(long, long, out long)` diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index e10fa3ca90c581..4abd1b935f0eb5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -179,6 +179,24 @@ public static nint MinValue get => unchecked((nint)nint_t.MinValue); } + /// Produces the full product of two unsigned native integers. + /// The integer to multiply with . + /// The integer to multiply with . + /// The lower half of the full product. + /// The upper half of the full product. + public static nint BigMul(nint left, nint right, out nint lower) + { +#if TARGET_64BIT + Int128 result = long.BigMul(left, right); + lower = (nint)result.Lower; + return (nint)result.Upper; +#else + long result = Math.BigMul((int)left, (int)right); + lower = (int)result; + return (int)(result >>> 32); +#endif + } + public int CompareTo(object? value) { if (value is nint other) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index b6b4e59bb5545d..b76e5a04f5ce11 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -1365,7 +1365,12 @@ static uint SubtractDivisor(Span left, ReadOnlySpan right, ulong q) return lower; } - internal static UInt128 BigMul(UInt128 left, UInt128 right, out UInt128 lower) + /// Produces the full product of two unsigned native integers. + /// The integer to multiply with . + /// The integer to multiply with . + /// The lower half of the full product. + /// The upper half of the full product. + public static UInt128 BigMul(UInt128 left, UInt128 right, out UInt128 lower) { // Adaptation of algorithm for multiplication // of 32-bit unsigned integers described diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index 7ab359b04199bc..d940ababf1c149 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -175,6 +175,24 @@ public static nuint MinValue get => unchecked((nuint)nuint_t.MinValue); } + /// Produces the full product of two unsigned native integers. + /// The integer to multiply with . + /// The integer to multiply with . + /// The lower half of the full product. + /// The upper half of the full product. + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { +#if TARGET_64BIT + UInt128 result = ulong.BigMul(left, right); + lower = (nuint)result.Lower; + return (nuint)result.Upper; +#else + ulong result = uint.BigMul((uint)left, (uint)right); + lower = (uint)result; + return (uint)(result >>> 32); +#endif + } + public int CompareTo(object? value) { if (value is nuint other) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index e27c05b00e3be4..ee744c9e438b5a 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -3574,6 +3574,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep static int System.Numerics.INumberBase.Radix { get { throw null; } } public static System.Int128 Zero { get { throw null; } } public static System.Int128 Abs(System.Int128 value) { throw null; } + public static System.Int128 BigMul(System.Int128 left, System.Int128 right, out System.Int128 lower) { throw null; } public static System.Int128 Clamp(System.Int128 value, System.Int128 min, System.Int128 max) { throw null; } public int CompareTo(System.Int128 value) { throw null; } public int CompareTo(object? value) { throw null; } @@ -4189,6 +4190,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep static nint System.Numerics.ISignedNumber.NegativeOne { get { throw null; } } public static nint Abs(nint value) { throw null; } public static nint Add(nint pointer, int offset) { throw null; } + public static nint BigMul(nint left, nint right, out nint lower) { throw null; } public static nint Clamp(nint value, nint min, nint max) { throw null; } public int CompareTo(nint value) { throw null; } public int CompareTo(object? value) { throw null; } @@ -6823,6 +6825,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) static System.UInt128 System.Numerics.IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } static int System.Numerics.INumberBase.Radix { get { throw null; } } public static System.UInt128 Zero { get { throw null; } } + public static System.UInt128 BigMul(System.UInt128 left, System.UInt128 right, out System.UInt128 lower) { throw null; } public static System.UInt128 Clamp(System.UInt128 value, System.UInt128 min, System.UInt128 max) { throw null; } public int CompareTo(object? value) { throw null; } public int CompareTo(System.UInt128 value) { throw null; } @@ -7443,6 +7446,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) static int System.Numerics.INumberBase.Radix { get { throw null; } } static nuint System.Numerics.INumberBase.Zero { get { throw null; } } public static nuint Add(nuint pointer, int offset) { throw null; } + public static nuint BigMul(nuint left, nuint right, out nuint lower) { throw null; } public static nuint Clamp(nuint value, nuint min, nuint max) { throw null; } public int CompareTo(object? value) { throw null; } public int CompareTo(nuint value) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.cs index 4bb3f0a73a5494..ab94d43f1355aa 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.cs @@ -562,5 +562,29 @@ public static void Runtime75416() Int128 b = (Int128.MaxValue - 10) * -100; Assert.Equal(b, +1100); } + + public static IEnumerable BigMul_TestData() + { + yield return new object[] { (Int128)0, (Int128)0, "0000000000000000000000000000000000000000000000000000000000000000" }; + yield return new object[] { (Int128)0, (Int128)1, "0000000000000000000000000000000000000000000000000000000000000000" }; + yield return new object[] { (Int128)1, (Int128)0, "0000000000000000000000000000000000000000000000000000000000000000" }; + yield return new object[] { (Int128)2, (Int128)3, "0000000000000000000000000000000000000000000000000000000000000006" }; + yield return new object[] { (Int128)3, (Int128)(-2), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA" }; + yield return new object[] { (Int128)(-1), (Int128)(-1), "0000000000000000000000000000000000000000000000000000000000000001" }; + yield return new object[] { (Int128)(-1), Int128.MinValue, "0000000000000000000000000000000080000000000000000000000000000000" }; + yield return new object[] { (Int128)1, Int128.MinValue, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000" }; + yield return new object[] { new Int128(0x7DD8FD06E61C42C7, 0x23B8308969A5D354), new Int128(0x23B8308969A5D354, 0x7DD8FD06E61C42C7), "118F366A0AEB79CDF61A2AD689A481BFBA9B9B874C9A440EB340AA067592EE4C" }; + yield return new object[] { new Int128(0x6DACB8FC835F41B5, 0xD26F1073812D6446), new Int128(0xD26F1073812D6446, 0x6DACB8FC835F41B5), "EC7A8BB31D6035AD30FC0550485D053B65FB0FB7A7D5A47F27742486E387AB7E" }; + yield return new object[] { new Int128(0xE990583BA9EAB3D8, 0x13CF93153370AB0B), new Int128(0x13CF93153370AB0B, 0xE990583BA9EAB3D8), "FE43855FCCDA31540755B833730F08FFD0B8FE2FF36A55181A45864AC9B70248" }; + yield return new object[] { new Int128(0xA85EB0478871B06C, 0xCC423B382BE5BB37), new Int128(0xCC423B382BE5BB37, 0xA85EB0478871B06C), "11B61855830A65CB4078E0668A2FEF9970B49ACA239DE4CFA363C1FE50E7CB34" }; + } + + [Theory] + [MemberData(nameof(BigMul_TestData))] + public static void BigMul(Int128 a, Int128 b, string result) + { + Int128 upper = Int128.BigMul(a, b, out Int128 lower); + Assert.Equal(result, $"{upper:X32}{lower:X32}"); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.cs index 9b2d96e9797fea..d56f15e69e410f 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.cs @@ -13,6 +13,7 @@ namespace System.Tests { public static class IntPtrTests { + private static unsafe bool Is32Bit => sizeof(void*) == 4; private static unsafe bool Is64Bit => sizeof(void*) == 8; [Fact] @@ -1074,5 +1075,43 @@ public static void ToString_C_EmptyPercentGroup_Success() [MemberData(nameof(ToString_TestData))] public static void TryFormat(nint i, string format, IFormatProvider provider, string expected) => NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); + + [ConditionalTheory(nameof(Is32Bit))] + [InlineData(0, 0, "0000000000000000")] + [InlineData(0, 1, "0000000000000000")] + [InlineData(1, 0, "0000000000000000")] + [InlineData(2, 3, "0000000000000006")] + [InlineData(3, -2, "FFFFFFFFFFFFFFFA")] + [InlineData(-1, -1, "0000000000000001")] + [InlineData(-1, int.MinValue, "0000000080000000")] + [InlineData(1, int.MinValue, "FFFFFFFF80000000")] + [InlineData(0x19E3BD39, 0x69A5D354, "0AAF2DC48A6D11B4")] + [InlineData(0x7CA0BE4B, -0x7ED29BBA, "C2425AAB1C785482")] + [InlineData(-0x56154C28, 0x3370AB0B, "EEB3DEFEC9B70248")] + [InlineData(-0x778E4F94, -0x541A44C9, "2746F6B050E7CB34")] + public static void BigMul32(int a, int b, string result) + { + nint upper = nint.BigMul(a, b, out nint lower); + Assert.Equal(result, $"{upper:X8}{lower:X8}"); + } + + [ConditionalTheory(nameof(Is64Bit))] + [InlineData(0L, 0L, "00000000000000000000000000000000")] + [InlineData(0L, 1L, "00000000000000000000000000000000")] + [InlineData(1L, 0L, "00000000000000000000000000000000")] + [InlineData(2L, 3L, "00000000000000000000000000000006")] + [InlineData(3L, -2L, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA")] + [InlineData(-1L, -1L, "00000000000000000000000000000001")] + [InlineData(-1L, long.MinValue, "00000000000000008000000000000000")] + [InlineData(1L, long.MinValue, "FFFFFFFFFFFFFFFF8000000000000000")] + [InlineData(0x7DD8FD06E61C42C7, 0x23B8308969A5D354, "118F366A0AEB79CDB340AA067592EE4C")] + [InlineData(0x6DACB8FC835F41B5, -0x2D90EF8C7ED29BBA, "EC7A8BB31D6035AD27742486E387AB7E")] + [InlineData(-0x166FA7C456154C28, 0x13CF93153370AB0B, "FE43855FCCDA31541A45864AC9B70248")] + [InlineData(-0x57A14FB8778E4F94, -0x33BDC4C7D41A44C9, "11B61855830A65CBA363C1FE50E7CB34")] + public static void BigMul64(long a, long b, string result) + { + nint upper = nint.BigMul((nint)a, (nint)b, out nint lower); + Assert.Equal(result, $"{upper:X16}{lower:X16}"); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs index d37f1d2a2c7bf6..a5d8716a2ecf2b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs @@ -496,5 +496,26 @@ public static void Runtime75416() UInt128 b = (UInt128Tests_GenericMath.Int128MaxValue - 10u) * (UInt128)(Int128)(-100); Assert.Equal(b, 1100u); } + + public static IEnumerable BigMul_TestData() + { + yield return new object[] { (UInt128)0, (UInt128)0, "0000000000000000000000000000000000000000000000000000000000000000" }; + yield return new object[] { (UInt128)0, (UInt128)1, "0000000000000000000000000000000000000000000000000000000000000000" }; + yield return new object[] { (UInt128)1, (UInt128)0, "0000000000000000000000000000000000000000000000000000000000000000" }; + yield return new object[] { (UInt128)2, (UInt128)3, "0000000000000000000000000000000000000000000000000000000000000006" }; + yield return new object[] { UInt128.MaxValue, (UInt128)2, "00000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" }; + yield return new object[] { UInt128.MaxValue, (UInt128)1, "00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" }; + yield return new object[] { UInt128.MaxValue, UInt128.MaxValue, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000001" }; + yield return new object[] { UInt128.MaxValue, (UInt128)3, "00000000000000000000000000000002FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD" }; + yield return new object[] { new UInt128(0xE8FAF08929B46BB5, 0x26B442D59782BA17), new UInt128(0x26B442D59782BA17, 0xE8FAF08929B46BB5), "23394CF891529663F897F2180AA9D1F6EA183EE8D7CCD26D1EB6255F4A612F43" }; + } + + [Theory] + [MemberData(nameof(BigMul_TestData))] + public static void BigMul(UInt128 a, UInt128 b, string result) + { + UInt128 upper = UInt128.BigMul(a, b, out UInt128 lower); + Assert.Equal(result, $"{upper:X32}{lower:X32}"); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.cs index 6978b9e44eb590..a1ff5ba221977e 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.cs @@ -13,6 +13,7 @@ namespace System.Tests { public static class UIntPtrTests { + private static unsafe bool Is32Bit => sizeof(void*) == 4; private static unsafe bool Is64Bit => sizeof(void*) == 8; [Fact] @@ -608,5 +609,37 @@ public static void Parse_Utf8Span_InvalidUtf8() [MemberData(nameof(ToString_TestData))] public static void TryFormat(nuint i, string format, IFormatProvider provider, string expected) => NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); + + [ConditionalTheory(nameof(Is32Bit))] + [InlineData(0U, 0U, "0000000000000000")] + [InlineData(0U, 1U, "0000000000000000")] + [InlineData(1U, 0U, "0000000000000000")] + [InlineData(2U, 3U, "0000000000000006")] + [InlineData(uint.MaxValue, 2U, "00000001FFFFFFFE")] + [InlineData(uint.MaxValue, 1U, "00000000FFFFFFFF")] + [InlineData(uint.MaxValue, uint.MaxValue, "FFFFFFFE00000001")] + [InlineData(uint.MaxValue, 3U, "00000002FFFFFFFD")] + [InlineData(0x29B46BB5U, 0x9782BA17U, "18AEB7774A612F43")] + public static void BigMul32(uint a, uint b, string result) + { + nuint upper = nuint.BigMul(a, b, out nuint lower); + Assert.Equal(result, $"{upper:X8}{lower:X8}"); + } + + [ConditionalTheory(nameof(Is64Bit))] + [InlineData(0U, 0U, "00000000000000000000000000000000")] + [InlineData(0U, 1U, "00000000000000000000000000000000")] + [InlineData(1U, 0U, "00000000000000000000000000000000")] + [InlineData(2U, 3U, "00000000000000000000000000000006")] + [InlineData(ulong.MaxValue, 2, "0000000000000001FFFFFFFFFFFFFFFE")] + [InlineData(ulong.MaxValue, 1, "0000000000000000FFFFFFFFFFFFFFFF")] + [InlineData(ulong.MaxValue, ulong.MaxValue, "FFFFFFFFFFFFFFFE0000000000000001")] + [InlineData(ulong.MaxValue, 3, "0000000000000002FFFFFFFFFFFFFFFD")] + [InlineData(0xE8FAF08929B46BB5, 0x26B442D59782BA17, "23394CF8915296631EB6255F4A612F43")] + public static void BigMul64(ulong a, ulong b, string result) + { + nuint upper = nuint.BigMul((nuint)a, (nuint)b, out nuint lower); + Assert.Equal(result, $"{upper:X16}{lower:X16}"); + } } }