From 03aa165167a87bb7f596c9283dd4fc8366f50012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viviana=20Due=C3=B1as=20Chavez?= Date: Mon, 23 Mar 2026 22:40:38 -0700 Subject: [PATCH 1/7] Log10 Interface definition in IBinaryInteger --- .../src/System/Numerics/IBinaryInteger.cs | 29 +++++++++++++++++++ .../System.Runtime/ref/System.Runtime.cs | 1 + 2 files changed, 30 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs index e44d1bdbd12e84..ed23fb3e9e55d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs @@ -260,6 +260,35 @@ static virtual TSelf Remainder(TSelf left, TSelf right, DivisionRounding mode) return remainder; } + /// Computes the integer logarithm base-10 of a value. + /// The value whose integer logarithm base-10 is to be computed. + /// The integer logarithm base-10 of . + /// is negative. + static virtual TSelf Log10(TSelf value) + { + if (!typeof(TSelf).IsValueType) + { + ArgumentNullException.ThrowIfNull(value); + } + + if (TSelf.IsNegative(value)) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + + // Slow loop: repeatedly divide by 10 + TSelf ten = TSelf.CreateChecked(10); + TSelf result = TSelf.Zero; + + while (value >= ten) + { + value /= ten; + result++; + } + + return result; + } + /// Computes the number of leading zero bits in a value. /// The value whose leading zero bits are to be counted. /// The number of leading zero bits in . diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index e49b44a7328794..c59d3914ecfdc7 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -11585,6 +11585,7 @@ public partial interface IBinaryInteger : System.IComparable, System.ICom static virtual TSelf Remainder(TSelf left, TSelf right, System.Numerics.DivisionRounding mode) { throw null; } int GetByteCount(); int GetShortestBitLength(); + static virtual TSelf Log10(TSelf value) { throw null; } static virtual TSelf LeadingZeroCount(TSelf value) { throw null; } static abstract TSelf PopCount(TSelf value); static virtual TSelf ReadBigEndian(byte[] source, bool isUnsigned) { throw null; } From 667ae735da3d0cce55f4d3dea4da73ec4fb29b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viviana=20Due=C3=B1as=20Chavez?= Date: Tue, 24 Mar 2026 15:19:22 -0700 Subject: [PATCH 2/7] Log10 in primitive types + tests --- .../Common/tests/System/GenericMathHelpers.cs | 2 + .../System.Private.CoreLib/src/System/Byte.cs | 3 ++ .../System.Private.CoreLib/src/System/Char.cs | 3 ++ .../src/System/Int128.cs | 10 ++++ .../src/System/Int16.cs | 10 ++++ .../src/System/Int32.cs | 11 ++++ .../src/System/Int64.cs | 11 ++++ .../src/System/IntPtr.cs | 15 ++++++ .../src/System/SByte.cs | 10 ++++ .../src/System/UInt128.cs | 53 +++++++++++++++++++ .../src/System/UInt16.cs | 3 ++ .../src/System/UInt32.cs | 28 ++++++++++ .../src/System/UInt64.cs | 35 ++++++++++++ .../src/System/UIntPtr.cs | 10 ++++ .../System.Runtime/ref/System.Runtime.cs | 13 +++++ .../System/ByteTests.GenericMath.cs | 12 +++++ .../System/CharTests.GenericMath.cs | 12 +++++ .../System/Int128Tests.GenericMath.cs | 10 ++++ .../System/Int16Tests.GenericMath.cs | 12 +++++ .../System/Int32Tests.GenericMath.cs | 12 +++++ .../System/Int64Tests.GenericMath.cs | 12 +++++ .../System/IntPtrTests.GenericMath.cs | 25 +++++++++ .../System/SByteTests.GenericMath.cs | 12 +++++ .../System/UInt128Tests.GenericMath.cs | 10 ++++ .../System/UInt16Tests.GenericMath.cs | 12 +++++ .../System/UInt32Tests.GenericMath.cs | 12 +++++ .../System/UInt64Tests.GenericMath.cs | 12 +++++ .../System/UIntPtrTests.GenericMath.cs | 25 +++++++++ 28 files changed, 395 insertions(+) diff --git a/src/libraries/Common/tests/System/GenericMathHelpers.cs b/src/libraries/Common/tests/System/GenericMathHelpers.cs index a3729f35b5a8fe..8a3550afaaaaca 100644 --- a/src/libraries/Common/tests/System/GenericMathHelpers.cs +++ b/src/libraries/Common/tests/System/GenericMathHelpers.cs @@ -64,6 +64,8 @@ public static TSelf RemainderExpected(TSelf left, TSelf right, DivisionRounding public static TSelf LeadingZeroCount(TSelf value) => TSelf.LeadingZeroCount(value); + public static TSelf Log10(TSelf value) => TSelf.Log10(value); + public static TSelf PopCount(TSelf value) => TSelf.PopCount(value); public static TSelf ReadBigEndian(byte[] source, bool isUnsigned) => TSelf.ReadBigEndian(source, isUnsigned); diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 1c933aaaced78b..971f5732ab7684 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -411,6 +411,9 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// public static bool IsPow2(byte value) => BitOperations.IsPow2((uint)value); + /// + public static byte Log10(byte value) => (byte)uint.Log10(value); + /// public static byte Log2(byte value) => (byte)BitOperations.Log2(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index e8ee0692a82d32..1aae3bb4438a8d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -1366,6 +1366,9 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// static bool IBinaryNumber.IsPow2(char value) => ushort.IsPow2(value); + /// + static char IBinaryInteger.Log10(char value) => (char)uint.Log10(value); + /// static char IBinaryNumber.Log2(char value) => (char)(ushort.Log2(value)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index bfc8e8c8bf0e25..3f0b981a14eab2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -969,6 +969,16 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(Int128 value) => (PopCount(value) == 1U) && IsPositive(value); + /// + public static Int128 Log10(Int128 value) + { + if (IsNegative(value)) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (Int128)UInt128.Log10((UInt128)value); + } + /// public static Int128 Log2(Int128 value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index b1a90999ea5e9e..c40bee1d7d80dc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -498,6 +498,16 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(short value) => BitOperations.IsPow2(value); + /// + public static short Log10(short value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (short)uint.Log10((uint)value); + } + /// public static short Log2(short value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 7a0cce55bc8099..3cf4cd8c5645d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -537,6 +537,17 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int by /// public static bool IsPow2(int value) => BitOperations.IsPow2(value); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Log10(int value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (int)uint.Log10((uint)value); + } + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index aa07dbb7d0327b..cc44a8dc227d93 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -534,6 +534,17 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// public static bool IsPow2(long value) => BitOperations.IsPow2(value); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Log10(long value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (long)ulong.Log10((ulong)value); + } + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index 4abd1b935f0eb5..966a6b7d84b4fc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -557,6 +557,21 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// public static bool IsPow2(nint value) => BitOperations.IsPow2(value); + /// + public static nint Log10(nint value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + + if (Environment.Is64BitProcess) + { + return (nint)ulong.Log10((ulong)value); + } + return (nint)uint.Log10((uint)value); + } + /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 4ba9c4372de61b..c5830d641ae7ed 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -461,6 +461,16 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(sbyte value) => BitOperations.IsPow2(value); + /// + public static sbyte Log10(sbyte value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (sbyte)uint.Log10((uint)value); + } + /// public static sbyte Log2(sbyte value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 20ad904bc423a2..370dadffe4ff6c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -979,6 +979,59 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out in /// public static bool IsPow2(UInt128 value) => PopCount(value) == 1U; + /// + public static UInt128 Log10(UInt128 value) + { + value |= 1U; + uint log2 = (uint)Log2(value) + 1; + uint approx = (log2 * 1233) >> 12; + return value < PowersOf10[(int)approx] ? approx - 1 : approx; + } + + // Lookup table for power-of-10 boundaries corrections + private static readonly UInt128[] PowersOf10 = + [ + new UInt128(0, 1UL), + new UInt128(0, 10UL), + new UInt128(0, 100UL), + new UInt128(0, 1_000UL), + new UInt128(0, 10_000UL), + new UInt128(0, 100_000UL), + new UInt128(0, 1_000_000UL), + new UInt128(0, 10_000_000UL), + new UInt128(0, 100_000_000UL), + new UInt128(0, 1_000_000_000UL), + new UInt128(0, 10_000_000_000UL), + new UInt128(0, 100_000_000_000UL), + new UInt128(0, 1_000_000_000_000UL), + new UInt128(0, 10_000_000_000_000UL), + new UInt128(0, 100_000_000_000_000UL), + new UInt128(0, 1_000_000_000_000_000UL), + new UInt128(0, 10_000_000_000_000_000UL), + new UInt128(0, 100_000_000_000_000_000UL), + new UInt128(0, 1_000_000_000_000_000_000UL), + new UInt128(0, 10_000_000_000_000_000_000UL), + new UInt128(5, 7766279631452241920UL), + new UInt128(54, 3875820019684212736UL), + new UInt128(542, 1864712049423024128UL), + new UInt128(5421, 200376420520689664UL), + new UInt128(54210, 2003764205206896640UL), + new UInt128(542101, 1590897978359414784UL), + new UInt128(5421010, 15908979783594147840UL), + new UInt128(54210108, 11515845246265065472UL), + new UInt128(542101086, 4477988020393345024UL), + new UInt128(5421010862, 7886392056514347008UL), + new UInt128(54210108624, 5076944270305263616UL), + new UInt128(542101086242, 13875954555633532928UL), + new UInt128(5421010862427, 9632337040368467968UL), + new UInt128(54210108624275, 4089650035136921600UL), + new UInt128(542101086242752, 4003012203950112768UL), + new UInt128(5421010862427522, 3136633892082024448UL), + new UInt128(54210108624275221, 12919594847110692864UL), + new UInt128(542101086242752217, 68739955140067328UL), + new UInt128(5421010862427522170, 687399551400673280UL), + ]; + /// public static UInt128 Log2(UInt128 value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 87d4f12a9a10d9..927bc7cca42c04 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -430,6 +430,9 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(ushort value) => BitOperations.IsPow2((uint)value); + /// + public static ushort Log10(ushort value) => (ushort)uint.Log10(value); + /// public static ushort Log2(ushort value) => (ushort)BitOperations.Log2(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index a774069fb560b2..176db94ca3a315 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -468,6 +468,34 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// public static bool IsPow2(uint value) => BitOperations.IsPow2(value); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Log10(uint value) + { + // Use Log2 to get approximate Log10 via the relationship: + // log10(x) ≈ (log2(x) + 1) * 1233 >> 12 + // Then correct with a powers-of-10 lookup table. + value |= 1; + uint log2 = (uint)BitOperations.Log2(value) + 1; + uint approx = (log2 * 1233) >> 12; + return value < PowersOf10[(int)approx] ? approx - 1 : approx; + } + + // Lookup table for power-of-10 boundaries corrections + private static ReadOnlySpan PowersOf10 => + [ + 1, + 10, + 100, + 1_000, + 10_000, + 100_000, + 1_000_000, + 10_000_000, + 100_000_000, + 1_000_000_000, + ]; + /// [Intrinsic] public static uint Log2(uint value) => (uint)BitOperations.Log2(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 56d9fc2cc2015a..789145e07216d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -467,6 +467,41 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(ulong value) => BitOperations.IsPow2(value); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Log10(ulong value) + { + value |= 1; + uint log2 = (uint)BitOperations.Log2(value) + 1; + uint approx = (log2 * 1233) >> 12; + return value < PowersOf10[(int)approx] ? approx - 1 : approx; + } + + // Lookup table for power-of-10 boundaries corrections + private static ReadOnlySpan PowersOf10 => + [ + 1, + 10, + 100, + 1_000, + 10_000, + 100_000, + 1_000_000, + 10_000_000, + 100_000_000, + 1_000_000_000, + 10_000_000_000, + 100_000_000_000, + 1_000_000_000_000, + 10_000_000_000_000, + 100_000_000_000_000, + 1_000_000_000_000_000, + 10_000_000_000_000_000, + 100_000_000_000_000_000, + 1_000_000_000_000_000_000, + 10_000_000_000_000_000_000, + ]; + /// [Intrinsic] public static ulong Log2(ulong value) => (ulong)BitOperations.Log2(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index 450160040fb4f7..e49fbb88bd6426 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -493,6 +493,16 @@ static nuint IBinaryNumber.AllBitsSet /// public static bool IsPow2(nuint value) => BitOperations.IsPow2(value); + /// + public static nuint Log10(nuint value) + { + if (Environment.Is64BitProcess) + { + return (nuint)ulong.Log10((ulong)value); + } + return (nuint)uint.Log10((uint)value); + } + /// [Intrinsic] public static nuint Log2(nuint value) => (nuint)BitOperations.Log2(value); diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index c59d3914ecfdc7..01571b85568d39 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -960,6 +960,7 @@ public static void SetByte(System.Array array, int index, byte value) { } public static bool IsOddInteger(byte value) { throw null; } public static bool IsPow2(byte value) { throw null; } public static byte LeadingZeroCount(byte value) { throw null; } + public static byte Log10(byte value) { throw null; } public static byte Log2(byte value) { throw null; } public static byte Max(byte x, byte y) { throw null; } public static byte Min(byte x, byte y) { throw null; } @@ -1177,6 +1178,7 @@ public CannotUnloadAppDomainException(string? message, System.Exception? innerEx int System.Numerics.IBinaryInteger.GetByteCount() { throw null; } int System.Numerics.IBinaryInteger.GetShortestBitLength() { throw null; } static char System.Numerics.IBinaryInteger.LeadingZeroCount(char value) { throw null; } + static char System.Numerics.IBinaryInteger.Log10(char value) { throw null; } static char System.Numerics.IBinaryInteger.PopCount(char value) { throw null; } static char System.Numerics.IBinaryInteger.RotateLeft(char value, int rotateAmount) { throw null; } static char System.Numerics.IBinaryInteger.RotateRight(char value, int rotateAmount) { throw null; } @@ -3607,6 +3609,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool IsPositive(System.Int128 value) { throw null; } public static bool IsPow2(System.Int128 value) { throw null; } public static System.Int128 LeadingZeroCount(System.Int128 value) { throw null; } + public static System.Int128 Log10(System.Int128 value) { throw null; } public static System.Int128 Log2(System.Int128 value) { throw null; } public static System.Int128 Max(System.Int128 x, System.Int128 y) { throw null; } public static System.Int128 MaxMagnitude(System.Int128 x, System.Int128 y) { throw null; } @@ -3794,6 +3797,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool IsPositive(short value) { throw null; } public static bool IsPow2(short value) { throw null; } public static short LeadingZeroCount(short value) { throw null; } + public static short Log10(short value) { throw null; } public static short Log2(short value) { throw null; } public static short Max(short x, short y) { throw null; } public static short MaxMagnitude(short x, short y) { throw null; } @@ -3935,6 +3939,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool IsPositive(int value) { throw null; } public static bool IsPow2(int value) { throw null; } public static int LeadingZeroCount(int value) { throw null; } + public static int Log10(int value) { throw null; } public static int Log2(int value) { throw null; } public static int Max(int x, int y) { throw null; } public static int MaxMagnitude(int x, int y) { throw null; } @@ -4076,6 +4081,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool IsPositive(long value) { throw null; } public static bool IsPow2(long value) { throw null; } public static long LeadingZeroCount(long value) { throw null; } + public static long Log10(long value) { throw null; } public static long Log2(long value) { throw null; } public static long Max(long x, long y) { throw null; } public static long MaxMagnitude(long x, long y) { throw null; } @@ -4223,6 +4229,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool IsPositive(nint value) { throw null; } public static bool IsPow2(nint value) { throw null; } public static nint LeadingZeroCount(nint value) { throw null; } + public static nint Log10(nint value) { throw null; } public static nint Log2(nint value) { throw null; } public static nint Max(nint x, nint y) { throw null; } public static nint MaxMagnitude(nint x, nint y) { throw null; } @@ -5236,6 +5243,7 @@ public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, S public static bool IsPositive(sbyte value) { throw null; } public static bool IsPow2(sbyte value) { throw null; } public static sbyte LeadingZeroCount(sbyte value) { throw null; } + public static sbyte Log10(sbyte value) { throw null; } public static sbyte Log2(sbyte value) { throw null; } public static sbyte Max(sbyte x, sbyte y) { throw null; } public static sbyte MaxMagnitude(sbyte x, sbyte y) { throw null; } @@ -6886,6 +6894,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool IsOddInteger(System.UInt128 value) { throw null; } public static bool IsPow2(System.UInt128 value) { throw null; } public static System.UInt128 LeadingZeroCount(System.UInt128 value) { throw null; } + public static System.UInt128 Log10(System.UInt128 value) { throw null; } public static System.UInt128 Log2(System.UInt128 value) { throw null; } public static System.UInt128 Max(System.UInt128 x, System.UInt128 y) { throw null; } public static System.UInt128 Min(System.UInt128 x, System.UInt128 y) { throw null; } @@ -7079,6 +7088,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool IsOddInteger(ushort value) { throw null; } public static bool IsPow2(ushort value) { throw null; } public static ushort LeadingZeroCount(ushort value) { throw null; } + public static ushort Log10(ushort value) { throw null; } public static ushort Log2(ushort value) { throw null; } public static ushort Max(ushort x, ushort y) { throw null; } public static ushort Min(ushort x, ushort y) { throw null; } @@ -7220,6 +7230,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool IsOddInteger(uint value) { throw null; } public static bool IsPow2(uint value) { throw null; } public static uint LeadingZeroCount(uint value) { throw null; } + public static uint Log10(uint value) { throw null; } public static uint Log2(uint value) { throw null; } public static uint Max(uint x, uint y) { throw null; } public static uint Min(uint x, uint y) { throw null; } @@ -7361,6 +7372,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool IsOddInteger(ulong value) { throw null; } public static bool IsPow2(ulong value) { throw null; } public static ulong LeadingZeroCount(ulong value) { throw null; } + public static ulong Log10(ulong value) { throw null; } public static ulong Log2(ulong value) { throw null; } public static ulong Max(ulong x, ulong y) { throw null; } public static ulong Min(ulong x, ulong y) { throw null; } @@ -7507,6 +7519,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool IsOddInteger(nuint value) { throw null; } public static bool IsPow2(nuint value) { throw null; } public static nuint LeadingZeroCount(nuint value) { throw null; } + public static nuint Log10(nuint value) { throw null; } public static nuint Log2(nuint value) { throw null; } public static nuint Max(nuint x, nuint y) { throw null; } public static nuint Min(nuint x, nuint y) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs index f61cac08977f1f..9691623dabbf3a 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs @@ -180,6 +180,18 @@ public static void LeadingZeroCountTest() Assert.Equal((byte)0x00, BinaryIntegerHelper.LeadingZeroCount((byte)0xFF)); } + [Fact] + public static void Log10Test() + { + Assert.Equal((byte)0x00, BinaryIntegerHelper.Log10((byte)0x00)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.Log10((byte)0x01)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.Log10((byte)0x0A)); + Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0x64)); + Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0x7F)); + Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0x80)); + Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0xFF)); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs index fbdc9862c29dc7..a19eeff2719cfd 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs @@ -179,6 +179,18 @@ public static void LeadingZeroCountTest() Assert.Equal((char)0x0000, BinaryIntegerHelper.LeadingZeroCount((char)0xFFFF)); } + [Fact] + public static void Log10Test() + { + Assert.Equal((char)0x0000, BinaryIntegerHelper.Log10((char)0x0000)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.Log10((char)0x0001)); + Assert.Equal((char)0x0001, BinaryIntegerHelper.Log10((char)0x000A)); + Assert.Equal((char)0x0002, BinaryIntegerHelper.Log10((char)0x0064)); + Assert.Equal((char)0x0004, BinaryIntegerHelper.Log10((char)0x7FFF)); + Assert.Equal((char)0x0004, BinaryIntegerHelper.Log10((char)0x8000)); + Assert.Equal((char)0x0004, BinaryIntegerHelper.Log10((char)0xFFFF)); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs index 6b3a9bc643b37f..77fff88566ac05 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs @@ -229,6 +229,16 @@ public static void LeadingZeroCountTest() Assert.Equal(0x00, BinaryIntegerHelper.LeadingZeroCount(NegativeOne)); } + [Fact] + public static void Log10Test() + { + Assert.Equal(0x00, BinaryIntegerHelper.Log10(Zero)); + Assert.Equal(0x00, BinaryIntegerHelper.Log10(One)); + Assert.Equal(0x26, BinaryIntegerHelper.Log10(MaxValue)); + Assert.Throws(() => BinaryIntegerHelper.Log10(MinValue)); + Assert.Throws(() => BinaryIntegerHelper.Log10(NegativeOne)); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs index c2c2baeadb585b..cd79c0a6790823 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs @@ -192,6 +192,18 @@ public static void LeadingZeroCountTest() Assert.Equal((short)0x0000, BinaryIntegerHelper.LeadingZeroCount(unchecked((short)0xFFFF))); } + [Fact] + public static void Log10Test() + { + Assert.Equal((short)0x0000, BinaryIntegerHelper.Log10((short)0x0000)); + Assert.Equal((short)0x0000, BinaryIntegerHelper.Log10((short)0x0001)); + Assert.Equal((short)0x0001, BinaryIntegerHelper.Log10((short)0x000A)); + Assert.Equal((short)0x0002, BinaryIntegerHelper.Log10((short)0x0064)); + Assert.Equal((short)0x0004, BinaryIntegerHelper.Log10((short)0x7FFF)); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((short)0x8000))); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((short)0xFFFF))); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs index 5d98e93a7084a8..d5dd781aa1ed09 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs @@ -192,6 +192,18 @@ public static void LeadingZeroCountTest() Assert.Equal((int)0x00000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((int)0xFFFFFFFF))); } + [Fact] + public static void Log10Test() + { + Assert.Equal((int)0x00000000, BinaryIntegerHelper.Log10((int)0x00000000)); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.Log10((int)0x00000001)); + Assert.Equal((int)0x00000001, BinaryIntegerHelper.Log10((int)0x0000000A)); + Assert.Equal((int)0x00000002, BinaryIntegerHelper.Log10((int)0x00000064)); + Assert.Equal((int)0x00000009, BinaryIntegerHelper.Log10((int)0x7FFFFFFF)); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((int)0x80000000))); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((int)0xFFFFFFFF))); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs index 46561b30232607..3c1809163d3d31 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs @@ -192,6 +192,18 @@ public static void LeadingZeroCountTest() Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((long)0xFFFFFFFFFFFFFFFF))); } + [Fact] + public static void Log10Test() + { + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.Log10((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.Log10((long)0x0000000000000001)); + Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.Log10((long)0x000000000000000A)); + Assert.Equal((long)0x0000000000000002, BinaryIntegerHelper.Log10((long)0x0000000000000064)); + Assert.Equal((long)0x0000000000000012, BinaryIntegerHelper.Log10((long)0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((long)0x8000000000000000))); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs index 18ab5ad3f276ae..64b9e702ada6e0 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs @@ -343,6 +343,31 @@ public static void LeadingZeroCountTest() } } + [Fact] + public static void Log10Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.Log10(unchecked((nint)0x000000000000000A))); + Assert.Equal(unchecked((nint)0x0000000000000002), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000064))); + Assert.Equal(unchecked((nint)0x0000000000000012), BinaryIntegerHelper.Log10(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.Log10((nint)0x00000000)); + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.Log10((nint)0x00000001)); + Assert.Equal((nint)0x00000001, BinaryIntegerHelper.Log10((nint)0x0000000A)); + Assert.Equal((nint)0x00000002, BinaryIntegerHelper.Log10((nint)0x00000064)); + Assert.Equal((nint)0x00000009, BinaryIntegerHelper.Log10((nint)0x7FFFFFFF)); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((nint)0x80000000))); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((nint)0xFFFFFFFF))); + } + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs index 82a0f58cbf5b3d..09eae2f16902e7 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs @@ -192,6 +192,18 @@ public static void LeadingZeroCountTest() Assert.Equal((sbyte)0x00, BinaryIntegerHelper.LeadingZeroCount(unchecked((sbyte)0xFF))); } + [Fact] + public static void Log10Test() + { + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.Log10((sbyte)0x00)); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.Log10((sbyte)0x01)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.Log10((sbyte)0x0A)); + Assert.Equal((sbyte)0x02, BinaryIntegerHelper.Log10((sbyte)0x64)); + Assert.Equal((sbyte)0x02, BinaryIntegerHelper.Log10((sbyte)0x7F)); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((sbyte)0x80))); + Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((sbyte)0xFF))); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs index 8492d36367bc03..ee621a4a516d2b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs @@ -230,6 +230,16 @@ public static void LeadingZeroCountTest() Assert.Equal(0x00U, BinaryIntegerHelper.LeadingZeroCount(MaxValue)); } + [Fact] + public static void Log10Test() + { + Assert.Equal(0x00U, BinaryIntegerHelper.Log10(Zero)); + Assert.Equal(0x00U, BinaryIntegerHelper.Log10(One)); + Assert.Equal(0x26U, BinaryIntegerHelper.Log10(Int128MaxValue)); + Assert.Equal(0x26U, BinaryIntegerHelper.Log10(Int128MaxValuePlusOne)); + Assert.Equal(0x26U, BinaryIntegerHelper.Log10(MaxValue)); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs index 38d0b2ccdb6b74..a276159a06acf5 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs @@ -180,6 +180,18 @@ public static void LeadingZeroCountTest() Assert.Equal((ushort)0x0000, BinaryIntegerHelper.LeadingZeroCount((ushort)0xFFFF)); } + [Fact] + public static void Log10Test() + { + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.Log10((ushort)0x0000)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.Log10((ushort)0x0001)); + Assert.Equal((ushort)0x0001, BinaryIntegerHelper.Log10((ushort)0x000A)); + Assert.Equal((ushort)0x0002, BinaryIntegerHelper.Log10((ushort)0x0064)); + Assert.Equal((ushort)0x0004, BinaryIntegerHelper.Log10((ushort)0x7FFF)); + Assert.Equal((ushort)0x0004, BinaryIntegerHelper.Log10((ushort)0x8000)); + Assert.Equal((ushort)0x0004, BinaryIntegerHelper.Log10((ushort)0xFFFF)); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs index b6ea65ed198988..f3f2d0dd197da2 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs @@ -181,6 +181,18 @@ public static void LeadingZeroCountTest() Assert.Equal((uint)0x00000000, BinaryIntegerHelper.LeadingZeroCount((uint)0xFFFFFFFF)); } + [Fact] + public static void Log10Test() + { + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.Log10((uint)0x00000000)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.Log10((uint)0x00000001)); + Assert.Equal((uint)0x00000001, BinaryIntegerHelper.Log10((uint)0x0000000A)); + Assert.Equal((uint)0x00000002, BinaryIntegerHelper.Log10((uint)0x00000064)); + Assert.Equal((uint)0x00000009, BinaryIntegerHelper.Log10((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x00000009, BinaryIntegerHelper.Log10((uint)0x80000000)); + Assert.Equal((uint)0x00000009, BinaryIntegerHelper.Log10((uint)0xFFFFFFFF)); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs index 87ed615d6ca5e0..716ab8d5798d22 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs @@ -188,6 +188,18 @@ public static void LeadingZeroCountTest() Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount((ulong)0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void Log10Test() + { + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.Log10((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.Log10((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.Log10((ulong)0x000000000000000A)); + Assert.Equal((ulong)0x0000000000000002, BinaryIntegerHelper.Log10((ulong)0x0000000000000064)); + Assert.Equal((ulong)0x0000000000000012, BinaryIntegerHelper.Log10((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x0000000000000012, BinaryIntegerHelper.Log10((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000013, BinaryIntegerHelper.Log10((ulong)0xFFFFFFFFFFFFFFFF)); + } + [Fact] public static void PopCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs index b67413444cbf80..e4838e492f79fd 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs @@ -351,6 +351,31 @@ public static void LeadingZeroCountTest() } } + [Fact] + public static void Log10Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.Log10(unchecked((nuint)0x000000000000000A))); + Assert.Equal(unchecked((nuint)0x0000000000000002), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000064))); + Assert.Equal(unchecked((nuint)0x0000000000000012), BinaryIntegerHelper.Log10(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x0000000000000012), BinaryIntegerHelper.Log10(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000013), BinaryIntegerHelper.Log10(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.Log10((nuint)0x00000000)); + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.Log10((nuint)0x00000001)); + Assert.Equal((nuint)0x00000001, BinaryIntegerHelper.Log10((nuint)0x0000000A)); + Assert.Equal((nuint)0x00000002, BinaryIntegerHelper.Log10((nuint)0x00000064)); + Assert.Equal((nuint)0x00000009, BinaryIntegerHelper.Log10((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x00000009, BinaryIntegerHelper.Log10((nuint)0x80000000)); + Assert.Equal((nuint)0x00000009, BinaryIntegerHelper.Log10((nuint)0xFFFFFFFF)); + } + } + [Fact] public static void PopCountTest() { From 7ba73a0e518ffcd9017555305a85c2588e107af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viviana=20Due=C3=B1as=20Chavez?= Date: Tue, 24 Mar 2026 17:43:50 -0700 Subject: [PATCH 3/7] BigInteger implementation, refactoring, add some edge cases in tests --- .../System.Private.CoreLib/src/System/Byte.cs | 6 +- .../System.Private.CoreLib/src/System/Char.cs | 6 +- .../src/System/Int128.cs | 20 ++-- .../src/System/Int16.cs | 20 ++-- .../src/System/Int32.cs | 22 ++-- .../src/System/Int64.cs | 22 ++-- .../src/System/IntPtr.cs | 30 ++--- .../src/System/SByte.cs | 20 ++-- .../src/System/UInt128.cs | 106 +++++++++--------- .../src/System/UInt16.cs | 6 +- .../src/System/UInt32.cs | 56 ++++----- .../src/System/UInt64.cs | 70 ++++++------ .../src/System/UIntPtr.cs | 20 ++-- .../ref/System.Runtime.Numerics.cs | 1 + .../src/System/Numerics/BigInteger.cs | 44 ++++++++ .../tests/BigIntegerTests.GenericMath.cs | 24 ++++ .../System/ByteTests.GenericMath.cs | 2 + .../System/CharTests.GenericMath.cs | 3 + .../System/Int128Tests.GenericMath.cs | 1 + .../System/Int16Tests.GenericMath.cs | 3 + .../System/Int32Tests.GenericMath.cs | 2 + .../System/Int64Tests.GenericMath.cs | 2 + .../System/IntPtrTests.GenericMath.cs | 2 + .../System/SByteTests.GenericMath.cs | 2 + .../System/UInt128Tests.GenericMath.cs | 1 + .../System/UInt16Tests.GenericMath.cs | 3 + .../System/UInt32Tests.GenericMath.cs | 2 + .../System/UInt64Tests.GenericMath.cs | 2 + .../System/UIntPtrTests.GenericMath.cs | 2 + 29 files changed, 298 insertions(+), 202 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 971f5732ab7684..331d28cf1d4304 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -281,6 +281,9 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) /// public static byte LeadingZeroCount(byte value) => (byte)(BitOperations.LeadingZeroCount(value) - 24); + /// + public static byte Log10(byte value) => (byte)uint.Log10(value); + /// public static byte PopCount(byte value) => (byte)BitOperations.PopCount(value); @@ -411,9 +414,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// public static bool IsPow2(byte value) => BitOperations.IsPow2((uint)value); - /// - public static byte Log10(byte value) => (byte)uint.Log10(value); - /// public static byte Log2(byte value) => (byte)BitOperations.Log2(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 1aae3bb4438a8d..42078fa8e05d94 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -1214,6 +1214,9 @@ public static int ConvertToUtf32(string s, int index) /// static char IBinaryInteger.LeadingZeroCount(char value) => (char)(BitOperations.LeadingZeroCount(value) - 16); + /// + static char IBinaryInteger.Log10(char value) => (char)uint.Log10(value); + /// static char IBinaryInteger.PopCount(char value) => (char)BitOperations.PopCount(value); @@ -1366,9 +1369,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// static bool IBinaryNumber.IsPow2(char value) => ushort.IsPow2(value); - /// - static char IBinaryInteger.Log10(char value) => (char)uint.Log10(value); - /// static char IBinaryNumber.Log2(char value) => (char)(ushort.Log2(value)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 3f0b981a14eab2..9387db8636ef71 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -726,6 +726,16 @@ private static int LeadingZeroCountAsInt32(Int128 value) return BitOperations.LeadingZeroCount(value._upper); } + /// + public static Int128 Log10(Int128 value) + { + if (IsNegative(value)) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (Int128)UInt128.Log10((UInt128)value); + } + /// public static Int128 PopCount(Int128 value) => ulong.PopCount(value._lower) + ulong.PopCount(value._upper); @@ -969,16 +979,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(Int128 value) => (PopCount(value) == 1U) && IsPositive(value); - /// - public static Int128 Log10(Int128 value) - { - if (IsNegative(value)) - { - ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); - } - return (Int128)UInt128.Log10((UInt128)value); - } - /// public static Int128 Log2(Int128 value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index c40bee1d7d80dc..c6312f3334c01a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -284,6 +284,16 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) /// public static short LeadingZeroCount(short value) => (short)(BitOperations.LeadingZeroCount((ushort)value) - 16); + /// + public static short Log10(short value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (short)uint.Log10((uint)value); + } + /// public static short PopCount(short value) => (short)BitOperations.PopCount((ushort)value); @@ -498,16 +508,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(short value) => BitOperations.IsPow2(value); - /// - public static short Log10(short value) - { - if (value < 0) - { - ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); - } - return (short)uint.Log10((uint)value); - } - /// public static short Log2(short value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 3cf4cd8c5645d9..b48c681f4b0105 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -300,6 +300,17 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) [Intrinsic] public static int LeadingZeroCount(int value) => BitOperations.LeadingZeroCount((uint)value); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Log10(int value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (int)uint.Log10((uint)value); + } + /// [Intrinsic] public static int PopCount(int value) => BitOperations.PopCount((uint)value); @@ -537,17 +548,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int by /// public static bool IsPow2(int value) => BitOperations.IsPow2(value); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Log10(int value) - { - if (value < 0) - { - ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); - } - return (int)uint.Log10((uint)value); - } - /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index cc44a8dc227d93..18c343fdac1808 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -297,6 +297,17 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) [Intrinsic] public static long LeadingZeroCount(long value) => BitOperations.LeadingZeroCount((ulong)value); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Log10(long value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (long)ulong.Log10((ulong)value); + } + /// [Intrinsic] public static long PopCount(long value) => BitOperations.PopCount((ulong)value); @@ -534,17 +545,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// public static bool IsPow2(long value) => BitOperations.IsPow2(value); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long Log10(long value) - { - if (value < 0) - { - ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); - } - return (long)ulong.Log10((ulong)value); - } - /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index 966a6b7d84b4fc..df9401eaf2a537 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -320,6 +320,21 @@ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatPro [Intrinsic] public static nint LeadingZeroCount(nint value) => BitOperations.LeadingZeroCount((nuint)value); + /// + public static nint Log10(nint value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + + if (Environment.Is64BitProcess) + { + return (nint)ulong.Log10((ulong)value); + } + return (nint)uint.Log10((uint)value); + } + /// [Intrinsic] public static nint PopCount(nint value) => BitOperations.PopCount((nuint)value); @@ -557,21 +572,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// public static bool IsPow2(nint value) => BitOperations.IsPow2(value); - /// - public static nint Log10(nint value) - { - if (value < 0) - { - ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); - } - - if (Environment.Is64BitProcess) - { - return (nint)ulong.Log10((ulong)value); - } - return (nint)uint.Log10((uint)value); - } - /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index c5830d641ae7ed..c22e77da475053 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -287,6 +287,16 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) /// public static sbyte LeadingZeroCount(sbyte value) => (sbyte)(BitOperations.LeadingZeroCount((byte)value) - 24); + /// + public static sbyte Log10(sbyte value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (sbyte)uint.Log10((uint)value); + } + /// public static sbyte PopCount(sbyte value) => (sbyte)BitOperations.PopCount((byte)value); @@ -461,16 +471,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(sbyte value) => BitOperations.IsPow2(value); - /// - public static sbyte Log10(sbyte value) - { - if (value < 0) - { - ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); - } - return (sbyte)uint.Log10((uint)value); - } - /// public static sbyte Log2(sbyte value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 370dadffe4ff6c..28293cb0f32c0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -797,6 +797,59 @@ private static int LeadingZeroCountAsInt32(UInt128 value) return BitOperations.LeadingZeroCount(value._upper); } + /// + public static UInt128 Log10(UInt128 value) + { + value |= 1U; + uint log2 = (uint)Log2(value) + 1; + uint approx = (log2 * 1233) >> 12; + return value < PowersOf10[(int)approx] ? approx - 1 : approx; + } + + // Lookup table for power-of-10 boundaries corrections + private static readonly UInt128[] PowersOf10 = + [ + new UInt128(0, 1UL), + new UInt128(0, 10UL), + new UInt128(0, 100UL), + new UInt128(0, 1_000UL), + new UInt128(0, 10_000UL), + new UInt128(0, 100_000UL), + new UInt128(0, 1_000_000UL), + new UInt128(0, 10_000_000UL), + new UInt128(0, 100_000_000UL), + new UInt128(0, 1_000_000_000UL), + new UInt128(0, 10_000_000_000UL), + new UInt128(0, 100_000_000_000UL), + new UInt128(0, 1_000_000_000_000UL), + new UInt128(0, 10_000_000_000_000UL), + new UInt128(0, 100_000_000_000_000UL), + new UInt128(0, 1_000_000_000_000_000UL), + new UInt128(0, 10_000_000_000_000_000UL), + new UInt128(0, 100_000_000_000_000_000UL), + new UInt128(0, 1_000_000_000_000_000_000UL), + new UInt128(0, 10_000_000_000_000_000_000UL), + new UInt128(5, 7766279631452241920UL), + new UInt128(54, 3875820019684212736UL), + new UInt128(542, 1864712049423024128UL), + new UInt128(5421, 200376420520689664UL), + new UInt128(54210, 2003764205206896640UL), + new UInt128(542101, 1590897978359414784UL), + new UInt128(5421010, 15908979783594147840UL), + new UInt128(54210108, 11515845246265065472UL), + new UInt128(542101086, 4477988020393345024UL), + new UInt128(5421010862, 7886392056514347008UL), + new UInt128(54210108624, 5076944270305263616UL), + new UInt128(542101086242, 13875954555633532928UL), + new UInt128(5421010862427, 9632337040368467968UL), + new UInt128(54210108624275, 4089650035136921600UL), + new UInt128(542101086242752, 4003012203950112768UL), + new UInt128(5421010862427522, 3136633892082024448UL), + new UInt128(54210108624275221, 12919594847110692864UL), + new UInt128(542101086242752217, 68739955140067328UL), + new UInt128(5421010862427522170, 687399551400673280UL), + ]; + /// public static UInt128 PopCount(UInt128 value) => ulong.PopCount(value._lower) + ulong.PopCount(value._upper); @@ -979,59 +1032,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out in /// public static bool IsPow2(UInt128 value) => PopCount(value) == 1U; - /// - public static UInt128 Log10(UInt128 value) - { - value |= 1U; - uint log2 = (uint)Log2(value) + 1; - uint approx = (log2 * 1233) >> 12; - return value < PowersOf10[(int)approx] ? approx - 1 : approx; - } - - // Lookup table for power-of-10 boundaries corrections - private static readonly UInt128[] PowersOf10 = - [ - new UInt128(0, 1UL), - new UInt128(0, 10UL), - new UInt128(0, 100UL), - new UInt128(0, 1_000UL), - new UInt128(0, 10_000UL), - new UInt128(0, 100_000UL), - new UInt128(0, 1_000_000UL), - new UInt128(0, 10_000_000UL), - new UInt128(0, 100_000_000UL), - new UInt128(0, 1_000_000_000UL), - new UInt128(0, 10_000_000_000UL), - new UInt128(0, 100_000_000_000UL), - new UInt128(0, 1_000_000_000_000UL), - new UInt128(0, 10_000_000_000_000UL), - new UInt128(0, 100_000_000_000_000UL), - new UInt128(0, 1_000_000_000_000_000UL), - new UInt128(0, 10_000_000_000_000_000UL), - new UInt128(0, 100_000_000_000_000_000UL), - new UInt128(0, 1_000_000_000_000_000_000UL), - new UInt128(0, 10_000_000_000_000_000_000UL), - new UInt128(5, 7766279631452241920UL), - new UInt128(54, 3875820019684212736UL), - new UInt128(542, 1864712049423024128UL), - new UInt128(5421, 200376420520689664UL), - new UInt128(54210, 2003764205206896640UL), - new UInt128(542101, 1590897978359414784UL), - new UInt128(5421010, 15908979783594147840UL), - new UInt128(54210108, 11515845246265065472UL), - new UInt128(542101086, 4477988020393345024UL), - new UInt128(5421010862, 7886392056514347008UL), - new UInt128(54210108624, 5076944270305263616UL), - new UInt128(542101086242, 13875954555633532928UL), - new UInt128(5421010862427, 9632337040368467968UL), - new UInt128(54210108624275, 4089650035136921600UL), - new UInt128(542101086242752, 4003012203950112768UL), - new UInt128(5421010862427522, 3136633892082024448UL), - new UInt128(54210108624275221, 12919594847110692864UL), - new UInt128(542101086242752217, 68739955140067328UL), - new UInt128(5421010862427522170, 687399551400673280UL), - ]; - /// public static UInt128 Log2(UInt128 value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 927bc7cca42c04..c477fcaaf56ad5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -278,6 +278,9 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) /// public static ushort LeadingZeroCount(ushort value) => (ushort)(BitOperations.LeadingZeroCount(value) - 16); + /// + public static ushort Log10(ushort value) => (ushort)uint.Log10(value); + /// public static ushort PopCount(ushort value) => (ushort)BitOperations.PopCount(value); @@ -430,9 +433,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(ushort value) => BitOperations.IsPow2((uint)value); - /// - public static ushort Log10(ushort value) => (ushort)uint.Log10(value); - /// public static ushort Log2(ushort value) => (ushort)BitOperations.Log2(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index 176db94ca3a315..f619fa149ac0c2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -295,6 +295,34 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) [Intrinsic] public static uint LeadingZeroCount(uint value) => (uint)BitOperations.LeadingZeroCount(value); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Log10(uint value) + { + // Use Log2 to get approximate Log10 via the relationship: + // log10(x) ≈ (log2(x) + 1) * 1233 >> 12 + // Then correct with a powers-of-10 lookup table. + value |= 1; + uint log2 = (uint)BitOperations.Log2(value) + 1; + uint approx = (log2 * 1233) >> 12; + return value < PowersOf10[(int)approx] ? approx - 1 : approx; + } + + // Lookup table for power-of-10 boundaries corrections + private static ReadOnlySpan PowersOf10 => + [ + 1, + 10, + 100, + 1_000, + 10_000, + 100_000, + 1_000_000, + 10_000_000, + 100_000_000, + 1_000_000_000, + ]; + /// [Intrinsic] public static uint PopCount(uint value) => (uint)BitOperations.PopCount(value); @@ -468,34 +496,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b /// public static bool IsPow2(uint value) => BitOperations.IsPow2(value); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Log10(uint value) - { - // Use Log2 to get approximate Log10 via the relationship: - // log10(x) ≈ (log2(x) + 1) * 1233 >> 12 - // Then correct with a powers-of-10 lookup table. - value |= 1; - uint log2 = (uint)BitOperations.Log2(value) + 1; - uint approx = (log2 * 1233) >> 12; - return value < PowersOf10[(int)approx] ? approx - 1 : approx; - } - - // Lookup table for power-of-10 boundaries corrections - private static ReadOnlySpan PowersOf10 => - [ - 1, - 10, - 100, - 1_000, - 10_000, - 100_000, - 1_000_000, - 10_000_000, - 100_000_000, - 1_000_000_000, - ]; - /// [Intrinsic] public static uint Log2(uint value) => (uint)BitOperations.Log2(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 789145e07216d9..c3d59c44b7471a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -294,6 +294,41 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) [Intrinsic] public static ulong LeadingZeroCount(ulong value) => (ulong)BitOperations.LeadingZeroCount(value); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Log10(ulong value) + { + value |= 1; + uint log2 = (uint)BitOperations.Log2(value) + 1; + uint approx = (log2 * 1233) >> 12; + return value < PowersOf10[(int)approx] ? approx - 1 : approx; + } + + // Lookup table for power-of-10 boundaries corrections + private static ReadOnlySpan PowersOf10 => + [ + 1, + 10, + 100, + 1_000, + 10_000, + 100_000, + 1_000_000, + 10_000_000, + 100_000_000, + 1_000_000_000, + 10_000_000_000, + 100_000_000_000, + 1_000_000_000_000, + 10_000_000_000_000, + 100_000_000_000_000, + 1_000_000_000_000_000, + 10_000_000_000_000_000, + 100_000_000_000_000_000, + 1_000_000_000_000_000_000, + 10_000_000_000_000_000_000, + ]; + /// [Intrinsic] public static ulong PopCount(ulong value) => (ulong)BitOperations.PopCount(value); @@ -467,41 +502,6 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int /// public static bool IsPow2(ulong value) => BitOperations.IsPow2(value); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong Log10(ulong value) - { - value |= 1; - uint log2 = (uint)BitOperations.Log2(value) + 1; - uint approx = (log2 * 1233) >> 12; - return value < PowersOf10[(int)approx] ? approx - 1 : approx; - } - - // Lookup table for power-of-10 boundaries corrections - private static ReadOnlySpan PowersOf10 => - [ - 1, - 10, - 100, - 1_000, - 10_000, - 100_000, - 1_000_000, - 10_000_000, - 100_000_000, - 1_000_000_000, - 10_000_000_000, - 100_000_000_000, - 1_000_000_000_000, - 10_000_000_000_000, - 100_000_000_000_000, - 1_000_000_000_000_000, - 10_000_000_000_000_000, - 100_000_000_000_000_000, - 1_000_000_000_000_000_000, - 10_000_000_000_000_000_000, - ]; - /// [Intrinsic] public static ulong Log2(ulong value) => (ulong)BitOperations.Log2(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index e49fbb88bd6426..6c9daa721a6084 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -316,6 +316,16 @@ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatPro [Intrinsic] public static nuint LeadingZeroCount(nuint value) => (nuint)BitOperations.LeadingZeroCount(value); + /// + public static nuint Log10(nuint value) + { + if (Environment.Is64BitProcess) + { + return (nuint)ulong.Log10((ulong)value); + } + return (nuint)uint.Log10((uint)value); + } + /// [Intrinsic] public static nuint PopCount(nuint value) => (nuint)BitOperations.PopCount(value); @@ -493,16 +503,6 @@ static nuint IBinaryNumber.AllBitsSet /// public static bool IsPow2(nuint value) => BitOperations.IsPow2(value); - /// - public static nuint Log10(nuint value) - { - if (Environment.Is64BitProcess) - { - return (nuint)ulong.Log10((ulong)value); - } - return (nuint)uint.Log10((uint)value); - } - /// [Intrinsic] public static nuint Log2(nuint value) => (nuint)BitOperations.Log2(value); diff --git a/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs b/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs index 9c3e42270020ca..e080d40f99ce11 100644 --- a/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs +++ b/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs @@ -200,6 +200,7 @@ namespace System.Numerics public static System.Numerics.BigInteger Subtract(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } int System.Numerics.IBinaryInteger.GetByteCount() { throw null; } int System.Numerics.IBinaryInteger.GetShortestBitLength() { throw null; } + static System.Numerics.BigInteger System.Numerics.IBinaryInteger.Log10(System.Numerics.BigInteger value) { throw null; } static bool System.Numerics.IBinaryInteger.TryReadBigEndian(System.ReadOnlySpan source, bool isUnsigned, out System.Numerics.BigInteger value) { throw null; } static bool System.Numerics.IBinaryInteger.TryReadLittleEndian(System.ReadOnlySpan source, bool isUnsigned, out System.Numerics.BigInteger value) { throw null; } bool System.Numerics.IBinaryInteger.TryWriteBigEndian(System.Span destination, out int bytesWritten) { throw null; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 2262614b9c4ee4..40437da16882cd 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -3181,6 +3181,50 @@ private void AssertValid() // IBinaryInteger // + /// + static BigInteger IBinaryInteger.Log10(BigInteger value) + { + value.AssertValid(); + + if (IsNegative(value)) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + + if (value._sign == 0) + { + return Zero; + } + + // For small values that fit in a uint, use the fast path + if (value._bits is null) + { + return ulong.Log10((ulong)(uint)value._sign); + } + + // For large values, use Log2-based estimation and correction + BigInteger log2Value = Log2(value); + // log10(x) ≈ log2(x) * log10(2) ≈ log2(x) * 1233 / 4096 + BigInteger approx = (log2Value * 1233) >> 12; + + // No upper size limit, so instead of a lookup table (like with uint's) + // correct by checking against 10^approx + BigInteger power = Pow(10, (int)approx); + + if (value < power) + { + return approx - One; + } + + // Check if we need to go one higher + if (value >= power * 10) + { + return approx + One; + } + + return approx; + } + /// public static (BigInteger Quotient, BigInteger Remainder) DivRem(BigInteger left, BigInteger right) { diff --git a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs index 5ce87ae4236c50..a2cd9dcc3090ff 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs @@ -206,6 +206,30 @@ public static void DivRemTest() Assert.Equal((Int64MaxValue, One), BinaryIntegerHelper.DivRem(UInt64MaxValue, 2)); } + [Fact] + public static void Log10Test() + { + Assert.Equal((BigInteger)0, BinaryIntegerHelper.Log10(Zero)); + Assert.Equal((BigInteger)0, BinaryIntegerHelper.Log10(One)); + Assert.Equal((BigInteger)0, BinaryIntegerHelper.Log10((BigInteger)9)); + Assert.Equal((BigInteger)1, BinaryIntegerHelper.Log10((BigInteger)10)); + Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)99)); + Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)100)); + Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)999)); + Assert.Equal((BigInteger)18, BinaryIntegerHelper.Log10(Int64MaxValue)); + Assert.Throws(() => BinaryIntegerHelper.Log10(Int64MinValue)); + } + + [Fact] + public static void Log10Test_LargeValues() + { + // 2^430000 has ~130K decimal digits. The approximation (Log2 * 1233) >> 12 + // accumulates enough error (~2) that the ±1 correction is insufficient. + // true log10(2^430000) = floor(430000 * log10(2)) = 129442 + BigInteger largeValue = BigInteger.Pow(2, 430000); + Assert.Equal((BigInteger)129442, BinaryIntegerHelper.Log10(largeValue)); + } + [Fact] public static void LeadingZeroCountTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs index 9691623dabbf3a..b0c47e8c7050b4 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs @@ -185,7 +185,9 @@ public static void Log10Test() { Assert.Equal((byte)0x00, BinaryIntegerHelper.Log10((byte)0x00)); Assert.Equal((byte)0x00, BinaryIntegerHelper.Log10((byte)0x01)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.Log10((byte)0x09)); Assert.Equal((byte)0x01, BinaryIntegerHelper.Log10((byte)0x0A)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.Log10((byte)0x63)); Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0x64)); Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0x7F)); Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0x80)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs index a19eeff2719cfd..c17a88685b9ed6 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs @@ -184,8 +184,11 @@ public static void Log10Test() { Assert.Equal((char)0x0000, BinaryIntegerHelper.Log10((char)0x0000)); Assert.Equal((char)0x0000, BinaryIntegerHelper.Log10((char)0x0001)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.Log10((char)0x0009)); Assert.Equal((char)0x0001, BinaryIntegerHelper.Log10((char)0x000A)); Assert.Equal((char)0x0002, BinaryIntegerHelper.Log10((char)0x0064)); + Assert.Equal((char)0x0002, BinaryIntegerHelper.Log10((char)0x03E7)); + Assert.Equal((char)0x0003, BinaryIntegerHelper.Log10((char)0x270F)); Assert.Equal((char)0x0004, BinaryIntegerHelper.Log10((char)0x7FFF)); Assert.Equal((char)0x0004, BinaryIntegerHelper.Log10((char)0x8000)); Assert.Equal((char)0x0004, BinaryIntegerHelper.Log10((char)0xFFFF)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs index 77fff88566ac05..9d8464eb937e57 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs @@ -234,6 +234,7 @@ public static void Log10Test() { Assert.Equal(0x00, BinaryIntegerHelper.Log10(Zero)); Assert.Equal(0x00, BinaryIntegerHelper.Log10(One)); + Assert.Equal(0x00, BinaryIntegerHelper.Log10((Int128)9)); Assert.Equal(0x26, BinaryIntegerHelper.Log10(MaxValue)); Assert.Throws(() => BinaryIntegerHelper.Log10(MinValue)); Assert.Throws(() => BinaryIntegerHelper.Log10(NegativeOne)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs index cd79c0a6790823..bdc98f1075ecf2 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs @@ -197,8 +197,11 @@ public static void Log10Test() { Assert.Equal((short)0x0000, BinaryIntegerHelper.Log10((short)0x0000)); Assert.Equal((short)0x0000, BinaryIntegerHelper.Log10((short)0x0001)); + Assert.Equal((short)0x0000, BinaryIntegerHelper.Log10((short)0x0009)); Assert.Equal((short)0x0001, BinaryIntegerHelper.Log10((short)0x000A)); Assert.Equal((short)0x0002, BinaryIntegerHelper.Log10((short)0x0064)); + Assert.Equal((short)0x0002, BinaryIntegerHelper.Log10((short)0x03E7)); + Assert.Equal((short)0x0003, BinaryIntegerHelper.Log10((short)0x270F)); Assert.Equal((short)0x0004, BinaryIntegerHelper.Log10((short)0x7FFF)); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((short)0x8000))); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((short)0xFFFF))); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs index d5dd781aa1ed09..ddabb0a8fae4a5 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs @@ -197,8 +197,10 @@ public static void Log10Test() { Assert.Equal((int)0x00000000, BinaryIntegerHelper.Log10((int)0x00000000)); Assert.Equal((int)0x00000000, BinaryIntegerHelper.Log10((int)0x00000001)); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.Log10((int)0x00000009)); Assert.Equal((int)0x00000001, BinaryIntegerHelper.Log10((int)0x0000000A)); Assert.Equal((int)0x00000002, BinaryIntegerHelper.Log10((int)0x00000064)); + Assert.Equal((int)0x00000008, BinaryIntegerHelper.Log10((int)0x3B9AC9FF)); Assert.Equal((int)0x00000009, BinaryIntegerHelper.Log10((int)0x7FFFFFFF)); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((int)0x80000000))); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((int)0xFFFFFFFF))); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs index 3c1809163d3d31..49a1e8d3fb0a96 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs @@ -197,8 +197,10 @@ public static void Log10Test() { Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.Log10((long)0x0000000000000000)); Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.Log10((long)0x0000000000000001)); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.Log10((long)0x0000000000000009)); Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.Log10((long)0x000000000000000A)); Assert.Equal((long)0x0000000000000002, BinaryIntegerHelper.Log10((long)0x0000000000000064)); + Assert.Equal((long)0x0000000000000011, BinaryIntegerHelper.Log10((long)0x0DE0B6B3A763FFFF)); Assert.Equal((long)0x0000000000000012, BinaryIntegerHelper.Log10((long)0x7FFFFFFFFFFFFFFF)); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((long)0x8000000000000000))); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((long)0xFFFFFFFFFFFFFFFF))); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs index 64b9e702ada6e0..a5f0de26afb45a 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs @@ -350,6 +350,7 @@ public static void Log10Test() { Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000000))); Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000009))); Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.Log10(unchecked((nint)0x000000000000000A))); Assert.Equal(unchecked((nint)0x0000000000000002), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000064))); Assert.Equal(unchecked((nint)0x0000000000000012), BinaryIntegerHelper.Log10(unchecked((nint)0x7FFFFFFFFFFFFFFF))); @@ -360,6 +361,7 @@ public static void Log10Test() { Assert.Equal((nint)0x00000000, BinaryIntegerHelper.Log10((nint)0x00000000)); Assert.Equal((nint)0x00000000, BinaryIntegerHelper.Log10((nint)0x00000001)); + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.Log10((nint)0x00000009)); Assert.Equal((nint)0x00000001, BinaryIntegerHelper.Log10((nint)0x0000000A)); Assert.Equal((nint)0x00000002, BinaryIntegerHelper.Log10((nint)0x00000064)); Assert.Equal((nint)0x00000009, BinaryIntegerHelper.Log10((nint)0x7FFFFFFF)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs index 09eae2f16902e7..326e7cae40ce8f 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs @@ -197,7 +197,9 @@ public static void Log10Test() { Assert.Equal((sbyte)0x00, BinaryIntegerHelper.Log10((sbyte)0x00)); Assert.Equal((sbyte)0x00, BinaryIntegerHelper.Log10((sbyte)0x01)); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.Log10((sbyte)0x09)); Assert.Equal((sbyte)0x01, BinaryIntegerHelper.Log10((sbyte)0x0A)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.Log10((sbyte)0x63)); Assert.Equal((sbyte)0x02, BinaryIntegerHelper.Log10((sbyte)0x64)); Assert.Equal((sbyte)0x02, BinaryIntegerHelper.Log10((sbyte)0x7F)); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((sbyte)0x80))); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs index ee621a4a516d2b..813ea18e1544d6 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs @@ -235,6 +235,7 @@ public static void Log10Test() { Assert.Equal(0x00U, BinaryIntegerHelper.Log10(Zero)); Assert.Equal(0x00U, BinaryIntegerHelper.Log10(One)); + Assert.Equal(0x00U, BinaryIntegerHelper.Log10((UInt128)9)); Assert.Equal(0x26U, BinaryIntegerHelper.Log10(Int128MaxValue)); Assert.Equal(0x26U, BinaryIntegerHelper.Log10(Int128MaxValuePlusOne)); Assert.Equal(0x26U, BinaryIntegerHelper.Log10(MaxValue)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs index a276159a06acf5..c25ab1db1bca9e 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs @@ -185,8 +185,11 @@ public static void Log10Test() { Assert.Equal((ushort)0x0000, BinaryIntegerHelper.Log10((ushort)0x0000)); Assert.Equal((ushort)0x0000, BinaryIntegerHelper.Log10((ushort)0x0001)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.Log10((ushort)0x0009)); Assert.Equal((ushort)0x0001, BinaryIntegerHelper.Log10((ushort)0x000A)); Assert.Equal((ushort)0x0002, BinaryIntegerHelper.Log10((ushort)0x0064)); + Assert.Equal((ushort)0x0002, BinaryIntegerHelper.Log10((ushort)0x03E7)); + Assert.Equal((ushort)0x0003, BinaryIntegerHelper.Log10((ushort)0x270F)); Assert.Equal((ushort)0x0004, BinaryIntegerHelper.Log10((ushort)0x7FFF)); Assert.Equal((ushort)0x0004, BinaryIntegerHelper.Log10((ushort)0x8000)); Assert.Equal((ushort)0x0004, BinaryIntegerHelper.Log10((ushort)0xFFFF)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs index f3f2d0dd197da2..76a801df05f7ff 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs @@ -186,8 +186,10 @@ public static void Log10Test() { Assert.Equal((uint)0x00000000, BinaryIntegerHelper.Log10((uint)0x00000000)); Assert.Equal((uint)0x00000000, BinaryIntegerHelper.Log10((uint)0x00000001)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.Log10((uint)0x00000009)); Assert.Equal((uint)0x00000001, BinaryIntegerHelper.Log10((uint)0x0000000A)); Assert.Equal((uint)0x00000002, BinaryIntegerHelper.Log10((uint)0x00000064)); + Assert.Equal((uint)0x00000008, BinaryIntegerHelper.Log10((uint)0x3B9AC9FF)); Assert.Equal((uint)0x00000009, BinaryIntegerHelper.Log10((uint)0x7FFFFFFF)); Assert.Equal((uint)0x00000009, BinaryIntegerHelper.Log10((uint)0x80000000)); Assert.Equal((uint)0x00000009, BinaryIntegerHelper.Log10((uint)0xFFFFFFFF)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs index 716ab8d5798d22..c2b2e069038707 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs @@ -193,8 +193,10 @@ public static void Log10Test() { Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.Log10((ulong)0x0000000000000000)); Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.Log10((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.Log10((ulong)0x0000000000000009)); Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.Log10((ulong)0x000000000000000A)); Assert.Equal((ulong)0x0000000000000002, BinaryIntegerHelper.Log10((ulong)0x0000000000000064)); + Assert.Equal((ulong)0x0000000000000012, BinaryIntegerHelper.Log10((ulong)0x8AC7230489E7FFFF)); Assert.Equal((ulong)0x0000000000000012, BinaryIntegerHelper.Log10((ulong)0x7FFFFFFFFFFFFFFF)); Assert.Equal((ulong)0x0000000000000012, BinaryIntegerHelper.Log10((ulong)0x8000000000000000)); Assert.Equal((ulong)0x0000000000000013, BinaryIntegerHelper.Log10((ulong)0xFFFFFFFFFFFFFFFF)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs index e4838e492f79fd..7850162d878c0f 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs @@ -358,6 +358,7 @@ public static void Log10Test() { Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000000))); Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000009))); Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.Log10(unchecked((nuint)0x000000000000000A))); Assert.Equal(unchecked((nuint)0x0000000000000002), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000064))); Assert.Equal(unchecked((nuint)0x0000000000000012), BinaryIntegerHelper.Log10(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); @@ -368,6 +369,7 @@ public static void Log10Test() { Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.Log10((nuint)0x00000000)); Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.Log10((nuint)0x00000001)); + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.Log10((nuint)0x00000009)); Assert.Equal((nuint)0x00000001, BinaryIntegerHelper.Log10((nuint)0x0000000A)); Assert.Equal((nuint)0x00000002, BinaryIntegerHelper.Log10((nuint)0x00000064)); Assert.Equal((nuint)0x00000009, BinaryIntegerHelper.Log10((nuint)0x7FFFFFFF)); From 1a7bcc8eb219e3e62847725a078e162490eef750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viviana=20Due=C3=B1as=20Chavez?= Date: Tue, 24 Mar 2026 18:08:51 -0700 Subject: [PATCH 4/7] Accumulated approximation error in BigInteger.Log10 --- .../src/System/Numerics/BigInteger.cs | 11 +++++----- .../tests/BigIntegerTests.GenericMath.cs | 21 +++++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 40437da16882cd..c28f1ce65bbefe 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -3211,15 +3211,16 @@ static BigInteger IBinaryInteger.Log10(BigInteger value) // correct by checking against 10^approx BigInteger power = Pow(10, (int)approx); - if (value < power) + while (value < power) { - return approx - One; + approx--; + power /= 10; } - // Check if we need to go one higher - if (value >= power * 10) + while (value >= power * 10) { - return approx + One; + approx++; + power *= 10; } return approx; diff --git a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs index a2cd9dcc3090ff..51cfa864ad3524 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs @@ -213,7 +213,7 @@ public static void Log10Test() Assert.Equal((BigInteger)0, BinaryIntegerHelper.Log10(One)); Assert.Equal((BigInteger)0, BinaryIntegerHelper.Log10((BigInteger)9)); Assert.Equal((BigInteger)1, BinaryIntegerHelper.Log10((BigInteger)10)); - Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)99)); + Assert.Equal((BigInteger)1, BinaryIntegerHelper.Log10((BigInteger)99)); Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)100)); Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)999)); Assert.Equal((BigInteger)18, BinaryIntegerHelper.Log10(Int64MaxValue)); @@ -223,11 +223,20 @@ public static void Log10Test() [Fact] public static void Log10Test_LargeValues() { - // 2^430000 has ~130K decimal digits. The approximation (Log2 * 1233) >> 12 - // accumulates enough error (~2) that the ±1 correction is insufficient. - // true log10(2^430000) = floor(430000 * log10(2)) = 129442 - BigInteger largeValue = BigInteger.Pow(2, 430000); - Assert.Equal((BigInteger)129442, BinaryIntegerHelper.Log10(largeValue)); + // 2^681 is the smallest power of 2 where the Log2-based approximation + // (Log2 * 1233 >> 12) undershoots by 1, exercising the correction loop. + // approx = 204, true log10 = 205 + Assert.Equal((BigInteger)205, BinaryIntegerHelper.Log10(BigInteger.Pow(2, 681))); + } + + [Fact] + [OuterLoop] + public static void Log10Test_VeryLargeValues() + { + // 2^217769 is the smallest power of 2 where the approximation undershoots + // by 2, which a simple ±1 correction could not handle. + // approx = 65553, true log10 = 65555 + Assert.Equal((BigInteger)65555, BinaryIntegerHelper.Log10(BigInteger.Pow(2, 217769))); } [Fact] From a215f874b11f782ee30d2f14ee55ba1a7452709a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viviana=20Due=C3=B1as=20Chavez?= Date: Thu, 2 Apr 2026 07:52:44 -0700 Subject: [PATCH 5/7] Address PR feedback --- .../src/System/IntPtr.cs | 6 +-- .../src/System/Numerics/IBinaryInteger.cs | 7 ++-- .../src/System/UInt128.cs | 2 + .../src/System/UInt32.cs | 1 + .../src/System/UInt64.cs | 2 + .../src/System/Numerics/BigInteger.cs | 6 ++- .../tests/BigIntegerTests.GenericMath.cs | 1 + .../System/ByteTests.GenericMath.cs | 18 ++++----- .../System/CharTests.GenericMath.cs | 20 +++++----- .../System/Int128Tests.GenericMath.cs | 8 ++-- .../System/Int16Tests.GenericMath.cs | 20 +++++----- .../System/Int32Tests.GenericMath.cs | 18 ++++----- .../System/Int64Tests.GenericMath.cs | 18 ++++----- .../System/IntPtrTests.GenericMath.cs | 24 ++++++------ .../System/SByteTests.GenericMath.cs | 18 ++++----- .../System/UInt128Tests.GenericMath.cs | 12 +++--- .../System/UInt16Tests.GenericMath.cs | 20 +++++----- .../System/UInt32Tests.GenericMath.cs | 18 ++++----- .../System/UInt64Tests.GenericMath.cs | 18 ++++----- .../System/UIntPtrTests.GenericMath.cs | 38 +++++++++---------- 20 files changed, 140 insertions(+), 135 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index df9401eaf2a537..cd6f768526f722 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -328,11 +328,7 @@ public static nint Log10(nint value) ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } - if (Environment.Is64BitProcess) - { - return (nint)ulong.Log10((ulong)value); - } - return (nint)uint.Log10((uint)value); + return (nint)nuint.Log10((nuint)value); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs index ed23fb3e9e55d9..8fde90d47b23c1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs @@ -260,10 +260,11 @@ static virtual TSelf Remainder(TSelf left, TSelf right, DivisionRounding mode) return remainder; } - /// Computes the integer logarithm base-10 of a value. - /// The value whose integer logarithm base-10 is to be computed. - /// The integer logarithm base-10 of . + /// Computes the integer logarithm base 10 of a value. + /// The value whose integer logarithm base 10 is to be computed. + /// The integer logarithm base 10 of . /// is negative. + /// The result of computing the integer logarithm base 10 of zero is zero. static virtual TSelf Log10(TSelf value) { if (!typeof(TSelf).IsValueType) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 28293cb0f32c0f..da447bda195a8f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -800,6 +800,8 @@ private static int LeadingZeroCountAsInt32(UInt128 value) /// public static UInt128 Log10(UInt128 value) { + // Approximate log10 via log2, then correct with a powers of 10 lookup table. + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 value |= 1U; uint log2 = (uint)Log2(value) + 1; uint approx = (log2 * 1233) >> 12; diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index f619fa149ac0c2..dc3282ad146444 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -302,6 +302,7 @@ public static uint Log10(uint value) // Use Log2 to get approximate Log10 via the relationship: // log10(x) ≈ (log2(x) + 1) * 1233 >> 12 // Then correct with a powers-of-10 lookup table. + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 value |= 1; uint log2 = (uint)BitOperations.Log2(value) + 1; uint approx = (log2 * 1233) >> 12; diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index c3d59c44b7471a..8abc0f1b5a4a9d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -298,6 +298,8 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Log10(ulong value) { + // Approximate log10 via log2, then correct with a powers of 10 lookup table. + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 value |= 1; uint log2 = (uint)BitOperations.Log2(value) + 1; uint approx = (log2 * 1233) >> 12; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 3534d1e5e783f9..05aa6f2d8153cf 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -3177,10 +3177,12 @@ static BigInteger IBinaryInteger.Log10(BigInteger value) power /= 10; } - while (value >= power * 10) + BigInteger nextPower = power * 10; + + while (value >= nextPower) { approx++; - power *= 10; + nextPower *= 10; } return approx; diff --git a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs index cd5fd3eea17faa..c56dc7de68b24f 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs @@ -217,6 +217,7 @@ public static void Log10Test() Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)100)); Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)999)); Assert.Equal((BigInteger)18, BinaryIntegerHelper.Log10(Int64MaxValue)); + Assert.Throws(() => BinaryIntegerHelper.Log10(NegativeOne)); Assert.Throws(() => BinaryIntegerHelper.Log10(Int64MinValue)); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs index b0c47e8c7050b4..c98230725abf21 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.GenericMath.cs @@ -183,15 +183,15 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal((byte)0x00, BinaryIntegerHelper.Log10((byte)0x00)); - Assert.Equal((byte)0x00, BinaryIntegerHelper.Log10((byte)0x01)); - Assert.Equal((byte)0x00, BinaryIntegerHelper.Log10((byte)0x09)); - Assert.Equal((byte)0x01, BinaryIntegerHelper.Log10((byte)0x0A)); - Assert.Equal((byte)0x01, BinaryIntegerHelper.Log10((byte)0x63)); - Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0x64)); - Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0x7F)); - Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0x80)); - Assert.Equal((byte)0x02, BinaryIntegerHelper.Log10((byte)0xFF)); + Assert.Equal((byte)0, BinaryIntegerHelper.Log10((byte)0)); + Assert.Equal((byte)0, BinaryIntegerHelper.Log10((byte)1)); + Assert.Equal((byte)0, BinaryIntegerHelper.Log10((byte)9)); + Assert.Equal((byte)1, BinaryIntegerHelper.Log10((byte)10)); + Assert.Equal((byte)1, BinaryIntegerHelper.Log10((byte)99)); + Assert.Equal((byte)2, BinaryIntegerHelper.Log10((byte)100)); + Assert.Equal((byte)2, BinaryIntegerHelper.Log10((byte)127)); + Assert.Equal((byte)2, BinaryIntegerHelper.Log10((byte)128)); + Assert.Equal((byte)2, BinaryIntegerHelper.Log10((byte)255)); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs index c17a88685b9ed6..c2fd0c2a468bfe 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs @@ -182,16 +182,16 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal((char)0x0000, BinaryIntegerHelper.Log10((char)0x0000)); - Assert.Equal((char)0x0000, BinaryIntegerHelper.Log10((char)0x0001)); - Assert.Equal((char)0x0000, BinaryIntegerHelper.Log10((char)0x0009)); - Assert.Equal((char)0x0001, BinaryIntegerHelper.Log10((char)0x000A)); - Assert.Equal((char)0x0002, BinaryIntegerHelper.Log10((char)0x0064)); - Assert.Equal((char)0x0002, BinaryIntegerHelper.Log10((char)0x03E7)); - Assert.Equal((char)0x0003, BinaryIntegerHelper.Log10((char)0x270F)); - Assert.Equal((char)0x0004, BinaryIntegerHelper.Log10((char)0x7FFF)); - Assert.Equal((char)0x0004, BinaryIntegerHelper.Log10((char)0x8000)); - Assert.Equal((char)0x0004, BinaryIntegerHelper.Log10((char)0xFFFF)); + Assert.Equal((char)0, BinaryIntegerHelper.Log10((char)0)); + Assert.Equal((char)0, BinaryIntegerHelper.Log10((char)1)); + Assert.Equal((char)0, BinaryIntegerHelper.Log10((char)9)); + Assert.Equal((char)1, BinaryIntegerHelper.Log10((char)10)); + Assert.Equal((char)2, BinaryIntegerHelper.Log10((char)100)); + Assert.Equal((char)2, BinaryIntegerHelper.Log10((char)999)); + Assert.Equal((char)3, BinaryIntegerHelper.Log10((char)9999)); + Assert.Equal((char)4, BinaryIntegerHelper.Log10((char)32767)); + Assert.Equal((char)4, BinaryIntegerHelper.Log10((char)32768)); + Assert.Equal((char)4, BinaryIntegerHelper.Log10((char)65535)); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs index 9d8464eb937e57..31f8bd01edaea8 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs @@ -232,10 +232,10 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal(0x00, BinaryIntegerHelper.Log10(Zero)); - Assert.Equal(0x00, BinaryIntegerHelper.Log10(One)); - Assert.Equal(0x00, BinaryIntegerHelper.Log10((Int128)9)); - Assert.Equal(0x26, BinaryIntegerHelper.Log10(MaxValue)); + Assert.Equal(0, BinaryIntegerHelper.Log10(Zero)); + Assert.Equal(0, BinaryIntegerHelper.Log10(One)); + Assert.Equal(0, BinaryIntegerHelper.Log10((Int128)9)); + Assert.Equal(38, BinaryIntegerHelper.Log10(MaxValue)); Assert.Throws(() => BinaryIntegerHelper.Log10(MinValue)); Assert.Throws(() => BinaryIntegerHelper.Log10(NegativeOne)); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs index bdc98f1075ecf2..9026b8e11bbe5e 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs @@ -195,16 +195,16 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal((short)0x0000, BinaryIntegerHelper.Log10((short)0x0000)); - Assert.Equal((short)0x0000, BinaryIntegerHelper.Log10((short)0x0001)); - Assert.Equal((short)0x0000, BinaryIntegerHelper.Log10((short)0x0009)); - Assert.Equal((short)0x0001, BinaryIntegerHelper.Log10((short)0x000A)); - Assert.Equal((short)0x0002, BinaryIntegerHelper.Log10((short)0x0064)); - Assert.Equal((short)0x0002, BinaryIntegerHelper.Log10((short)0x03E7)); - Assert.Equal((short)0x0003, BinaryIntegerHelper.Log10((short)0x270F)); - Assert.Equal((short)0x0004, BinaryIntegerHelper.Log10((short)0x7FFF)); - Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((short)0x8000))); - Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((short)0xFFFF))); + Assert.Equal((short)0, BinaryIntegerHelper.Log10((short)0)); + Assert.Equal((short)0, BinaryIntegerHelper.Log10((short)1)); + Assert.Equal((short)0, BinaryIntegerHelper.Log10((short)9)); + Assert.Equal((short)1, BinaryIntegerHelper.Log10((short)10)); + Assert.Equal((short)2, BinaryIntegerHelper.Log10((short)100)); + Assert.Equal((short)2, BinaryIntegerHelper.Log10((short)999)); + Assert.Equal((short)3, BinaryIntegerHelper.Log10((short)9999)); + Assert.Equal((short)4, BinaryIntegerHelper.Log10((short)32767)); + Assert.Throws(() => BinaryIntegerHelper.Log10((short)(-32768))); + Assert.Throws(() => BinaryIntegerHelper.Log10((short)(-1))); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs index ddabb0a8fae4a5..30fa68fcef2e05 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs @@ -195,15 +195,15 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal((int)0x00000000, BinaryIntegerHelper.Log10((int)0x00000000)); - Assert.Equal((int)0x00000000, BinaryIntegerHelper.Log10((int)0x00000001)); - Assert.Equal((int)0x00000000, BinaryIntegerHelper.Log10((int)0x00000009)); - Assert.Equal((int)0x00000001, BinaryIntegerHelper.Log10((int)0x0000000A)); - Assert.Equal((int)0x00000002, BinaryIntegerHelper.Log10((int)0x00000064)); - Assert.Equal((int)0x00000008, BinaryIntegerHelper.Log10((int)0x3B9AC9FF)); - Assert.Equal((int)0x00000009, BinaryIntegerHelper.Log10((int)0x7FFFFFFF)); - Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((int)0x80000000))); - Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((int)0xFFFFFFFF))); + Assert.Equal(0, BinaryIntegerHelper.Log10(0)); + Assert.Equal(0, BinaryIntegerHelper.Log10(1)); + Assert.Equal(0, BinaryIntegerHelper.Log10(9)); + Assert.Equal(1, BinaryIntegerHelper.Log10(10)); + Assert.Equal(2, BinaryIntegerHelper.Log10(100)); + Assert.Equal(8, BinaryIntegerHelper.Log10(999_999_999)); + Assert.Equal(9, BinaryIntegerHelper.Log10(2_147_483_647)); + Assert.Throws(() => BinaryIntegerHelper.Log10(-2_147_483_648)); + Assert.Throws(() => BinaryIntegerHelper.Log10(-1)); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs index 49a1e8d3fb0a96..f9fa23322eae4c 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs @@ -195,15 +195,15 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.Log10((long)0x0000000000000000)); - Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.Log10((long)0x0000000000000001)); - Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.Log10((long)0x0000000000000009)); - Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.Log10((long)0x000000000000000A)); - Assert.Equal((long)0x0000000000000002, BinaryIntegerHelper.Log10((long)0x0000000000000064)); - Assert.Equal((long)0x0000000000000011, BinaryIntegerHelper.Log10((long)0x0DE0B6B3A763FFFF)); - Assert.Equal((long)0x0000000000000012, BinaryIntegerHelper.Log10((long)0x7FFFFFFFFFFFFFFF)); - Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((long)0x8000000000000000))); - Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((long)0xFFFFFFFFFFFFFFFF))); + Assert.Equal((long)0, BinaryIntegerHelper.Log10((long)0)); + Assert.Equal((long)0, BinaryIntegerHelper.Log10((long)1)); + Assert.Equal((long)0, BinaryIntegerHelper.Log10((long)9)); + Assert.Equal((long)1, BinaryIntegerHelper.Log10((long)10)); + Assert.Equal((long)2, BinaryIntegerHelper.Log10((long)100)); + Assert.Equal((long)17, BinaryIntegerHelper.Log10((long)999_999_999_999_999_999)); + Assert.Equal((long)18, BinaryIntegerHelper.Log10((long)9_223_372_036_854_775_807)); + Assert.Throws(() => BinaryIntegerHelper.Log10(-9_223_372_036_854_775_808)); + Assert.Throws(() => BinaryIntegerHelper.Log10(-1)); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs index a5f0de26afb45a..cfac7e4164c2aa 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/IntPtrTests.GenericMath.cs @@ -348,23 +348,23 @@ public static void Log10Test() { if (Environment.Is64BitProcess) { - Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000000))); - Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000001))); - Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000009))); - Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.Log10(unchecked((nint)0x000000000000000A))); - Assert.Equal(unchecked((nint)0x0000000000000002), BinaryIntegerHelper.Log10(unchecked((nint)0x0000000000000064))); - Assert.Equal(unchecked((nint)0x0000000000000012), BinaryIntegerHelper.Log10(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0), BinaryIntegerHelper.Log10(unchecked((nint)0))); + Assert.Equal(unchecked((nint)0), BinaryIntegerHelper.Log10(unchecked((nint)1))); + Assert.Equal(unchecked((nint)0), BinaryIntegerHelper.Log10(unchecked((nint)9))); + Assert.Equal(unchecked((nint)1), BinaryIntegerHelper.Log10(unchecked((nint)10))); + Assert.Equal(unchecked((nint)2), BinaryIntegerHelper.Log10(unchecked((nint)100))); + Assert.Equal(unchecked((nint)18), BinaryIntegerHelper.Log10(unchecked((nint)9_223_372_036_854_775_807))); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((nint)0x8000000000000000))); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((nint)0xFFFFFFFFFFFFFFFF))); } else { - Assert.Equal((nint)0x00000000, BinaryIntegerHelper.Log10((nint)0x00000000)); - Assert.Equal((nint)0x00000000, BinaryIntegerHelper.Log10((nint)0x00000001)); - Assert.Equal((nint)0x00000000, BinaryIntegerHelper.Log10((nint)0x00000009)); - Assert.Equal((nint)0x00000001, BinaryIntegerHelper.Log10((nint)0x0000000A)); - Assert.Equal((nint)0x00000002, BinaryIntegerHelper.Log10((nint)0x00000064)); - Assert.Equal((nint)0x00000009, BinaryIntegerHelper.Log10((nint)0x7FFFFFFF)); + Assert.Equal((nint)0, BinaryIntegerHelper.Log10((nint)0)); + Assert.Equal((nint)0, BinaryIntegerHelper.Log10((nint)1)); + Assert.Equal((nint)0, BinaryIntegerHelper.Log10((nint)9)); + Assert.Equal((nint)1, BinaryIntegerHelper.Log10((nint)10)); + Assert.Equal((nint)2, BinaryIntegerHelper.Log10((nint)100)); + Assert.Equal((nint)9, BinaryIntegerHelper.Log10((nint)2_147_483_647)); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((nint)0x80000000))); Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((nint)0xFFFFFFFF))); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs index 326e7cae40ce8f..48412bf11c52ee 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.GenericMath.cs @@ -195,15 +195,15 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal((sbyte)0x00, BinaryIntegerHelper.Log10((sbyte)0x00)); - Assert.Equal((sbyte)0x00, BinaryIntegerHelper.Log10((sbyte)0x01)); - Assert.Equal((sbyte)0x00, BinaryIntegerHelper.Log10((sbyte)0x09)); - Assert.Equal((sbyte)0x01, BinaryIntegerHelper.Log10((sbyte)0x0A)); - Assert.Equal((sbyte)0x01, BinaryIntegerHelper.Log10((sbyte)0x63)); - Assert.Equal((sbyte)0x02, BinaryIntegerHelper.Log10((sbyte)0x64)); - Assert.Equal((sbyte)0x02, BinaryIntegerHelper.Log10((sbyte)0x7F)); - Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((sbyte)0x80))); - Assert.Throws(() => BinaryIntegerHelper.Log10(unchecked((sbyte)0xFF))); + Assert.Equal((sbyte)0, BinaryIntegerHelper.Log10((sbyte)0)); + Assert.Equal((sbyte)0, BinaryIntegerHelper.Log10((sbyte)1)); + Assert.Equal((sbyte)0, BinaryIntegerHelper.Log10((sbyte)9)); + Assert.Equal((sbyte)1, BinaryIntegerHelper.Log10((sbyte)10)); + Assert.Equal((sbyte)1, BinaryIntegerHelper.Log10((sbyte)99)); + Assert.Equal((sbyte)2, BinaryIntegerHelper.Log10((sbyte)100)); + Assert.Equal((sbyte)2, BinaryIntegerHelper.Log10((sbyte)127)); + Assert.Throws(() => BinaryIntegerHelper.Log10((sbyte)(-128))); + Assert.Throws(() => BinaryIntegerHelper.Log10((sbyte)(-1))); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs index 813ea18e1544d6..906ef8b140da87 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs @@ -233,12 +233,12 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal(0x00U, BinaryIntegerHelper.Log10(Zero)); - Assert.Equal(0x00U, BinaryIntegerHelper.Log10(One)); - Assert.Equal(0x00U, BinaryIntegerHelper.Log10((UInt128)9)); - Assert.Equal(0x26U, BinaryIntegerHelper.Log10(Int128MaxValue)); - Assert.Equal(0x26U, BinaryIntegerHelper.Log10(Int128MaxValuePlusOne)); - Assert.Equal(0x26U, BinaryIntegerHelper.Log10(MaxValue)); + Assert.Equal(0U, BinaryIntegerHelper.Log10(Zero)); + Assert.Equal(0U, BinaryIntegerHelper.Log10(One)); + Assert.Equal(0U, BinaryIntegerHelper.Log10((UInt128)9)); + Assert.Equal(38U, BinaryIntegerHelper.Log10(Int128MaxValue)); + Assert.Equal(38U, BinaryIntegerHelper.Log10(Int128MaxValuePlusOne)); + Assert.Equal(38U, BinaryIntegerHelper.Log10(MaxValue)); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs index c25ab1db1bca9e..d57d63e05abcfd 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs @@ -183,16 +183,16 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal((ushort)0x0000, BinaryIntegerHelper.Log10((ushort)0x0000)); - Assert.Equal((ushort)0x0000, BinaryIntegerHelper.Log10((ushort)0x0001)); - Assert.Equal((ushort)0x0000, BinaryIntegerHelper.Log10((ushort)0x0009)); - Assert.Equal((ushort)0x0001, BinaryIntegerHelper.Log10((ushort)0x000A)); - Assert.Equal((ushort)0x0002, BinaryIntegerHelper.Log10((ushort)0x0064)); - Assert.Equal((ushort)0x0002, BinaryIntegerHelper.Log10((ushort)0x03E7)); - Assert.Equal((ushort)0x0003, BinaryIntegerHelper.Log10((ushort)0x270F)); - Assert.Equal((ushort)0x0004, BinaryIntegerHelper.Log10((ushort)0x7FFF)); - Assert.Equal((ushort)0x0004, BinaryIntegerHelper.Log10((ushort)0x8000)); - Assert.Equal((ushort)0x0004, BinaryIntegerHelper.Log10((ushort)0xFFFF)); + Assert.Equal((ushort)0, BinaryIntegerHelper.Log10((ushort)0)); + Assert.Equal((ushort)0, BinaryIntegerHelper.Log10((ushort)1)); + Assert.Equal((ushort)0, BinaryIntegerHelper.Log10((ushort)9)); + Assert.Equal((ushort)1, BinaryIntegerHelper.Log10((ushort)10)); + Assert.Equal((ushort)2, BinaryIntegerHelper.Log10((ushort)100)); + Assert.Equal((ushort)2, BinaryIntegerHelper.Log10((ushort)999)); + Assert.Equal((ushort)3, BinaryIntegerHelper.Log10((ushort)9999)); + Assert.Equal((ushort)4, BinaryIntegerHelper.Log10((ushort)32767)); + Assert.Equal((ushort)4, BinaryIntegerHelper.Log10((ushort)32768)); + Assert.Equal((ushort)4, BinaryIntegerHelper.Log10((ushort)65535)); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs index 76a801df05f7ff..aa2a16fa7cd6db 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs @@ -184,15 +184,15 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal((uint)0x00000000, BinaryIntegerHelper.Log10((uint)0x00000000)); - Assert.Equal((uint)0x00000000, BinaryIntegerHelper.Log10((uint)0x00000001)); - Assert.Equal((uint)0x00000000, BinaryIntegerHelper.Log10((uint)0x00000009)); - Assert.Equal((uint)0x00000001, BinaryIntegerHelper.Log10((uint)0x0000000A)); - Assert.Equal((uint)0x00000002, BinaryIntegerHelper.Log10((uint)0x00000064)); - Assert.Equal((uint)0x00000008, BinaryIntegerHelper.Log10((uint)0x3B9AC9FF)); - Assert.Equal((uint)0x00000009, BinaryIntegerHelper.Log10((uint)0x7FFFFFFF)); - Assert.Equal((uint)0x00000009, BinaryIntegerHelper.Log10((uint)0x80000000)); - Assert.Equal((uint)0x00000009, BinaryIntegerHelper.Log10((uint)0xFFFFFFFF)); + Assert.Equal((uint)0, BinaryIntegerHelper.Log10((uint)0)); + Assert.Equal((uint)0, BinaryIntegerHelper.Log10((uint)1)); + Assert.Equal((uint)0, BinaryIntegerHelper.Log10((uint)9)); + Assert.Equal((uint)1, BinaryIntegerHelper.Log10((uint)10)); + Assert.Equal((uint)2, BinaryIntegerHelper.Log10((uint)100)); + Assert.Equal((uint)8, BinaryIntegerHelper.Log10((uint)999_999_999)); + Assert.Equal((uint)9, BinaryIntegerHelper.Log10((uint)2_147_483_647)); + Assert.Equal((uint)9, BinaryIntegerHelper.Log10((uint)2_147_483_648)); + Assert.Equal((uint)9, BinaryIntegerHelper.Log10((uint)4_294_967_295)); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs index c2b2e069038707..37291ccead42d3 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs @@ -191,15 +191,15 @@ public static void LeadingZeroCountTest() [Fact] public static void Log10Test() { - Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.Log10((ulong)0x0000000000000000)); - Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.Log10((ulong)0x0000000000000001)); - Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.Log10((ulong)0x0000000000000009)); - Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.Log10((ulong)0x000000000000000A)); - Assert.Equal((ulong)0x0000000000000002, BinaryIntegerHelper.Log10((ulong)0x0000000000000064)); - Assert.Equal((ulong)0x0000000000000012, BinaryIntegerHelper.Log10((ulong)0x8AC7230489E7FFFF)); - Assert.Equal((ulong)0x0000000000000012, BinaryIntegerHelper.Log10((ulong)0x7FFFFFFFFFFFFFFF)); - Assert.Equal((ulong)0x0000000000000012, BinaryIntegerHelper.Log10((ulong)0x8000000000000000)); - Assert.Equal((ulong)0x0000000000000013, BinaryIntegerHelper.Log10((ulong)0xFFFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0, BinaryIntegerHelper.Log10((ulong)0)); + Assert.Equal((ulong)0, BinaryIntegerHelper.Log10((ulong)1)); + Assert.Equal((ulong)0, BinaryIntegerHelper.Log10((ulong)9)); + Assert.Equal((ulong)1, BinaryIntegerHelper.Log10((ulong)10)); + Assert.Equal((ulong)2, BinaryIntegerHelper.Log10((ulong)100)); + Assert.Equal((ulong)18, BinaryIntegerHelper.Log10((ulong)9_999_999_999_999_999_999)); + Assert.Equal((ulong)18, BinaryIntegerHelper.Log10((ulong)9_223_372_036_854_775_807)); + Assert.Equal((ulong)18, BinaryIntegerHelper.Log10((ulong)9_223_372_036_854_775_808)); + Assert.Equal((ulong)19, BinaryIntegerHelper.Log10((ulong)18_446_744_073_709_551_615)); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs index 7850162d878c0f..4a3b6236ad1e99 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UIntPtrTests.GenericMath.cs @@ -356,25 +356,25 @@ public static void Log10Test() { if (Environment.Is64BitProcess) { - Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000000))); - Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000001))); - Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000009))); - Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.Log10(unchecked((nuint)0x000000000000000A))); - Assert.Equal(unchecked((nuint)0x0000000000000002), BinaryIntegerHelper.Log10(unchecked((nuint)0x0000000000000064))); - Assert.Equal(unchecked((nuint)0x0000000000000012), BinaryIntegerHelper.Log10(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); - Assert.Equal(unchecked((nuint)0x0000000000000012), BinaryIntegerHelper.Log10(unchecked((nuint)0x8000000000000000))); - Assert.Equal(unchecked((nuint)0x0000000000000013), BinaryIntegerHelper.Log10(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); - } - else - { - Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.Log10((nuint)0x00000000)); - Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.Log10((nuint)0x00000001)); - Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.Log10((nuint)0x00000009)); - Assert.Equal((nuint)0x00000001, BinaryIntegerHelper.Log10((nuint)0x0000000A)); - Assert.Equal((nuint)0x00000002, BinaryIntegerHelper.Log10((nuint)0x00000064)); - Assert.Equal((nuint)0x00000009, BinaryIntegerHelper.Log10((nuint)0x7FFFFFFF)); - Assert.Equal((nuint)0x00000009, BinaryIntegerHelper.Log10((nuint)0x80000000)); - Assert.Equal((nuint)0x00000009, BinaryIntegerHelper.Log10((nuint)0xFFFFFFFF)); + Assert.Equal(unchecked((nuint)0), BinaryIntegerHelper.Log10(unchecked((nuint)0))); + Assert.Equal(unchecked((nuint)0), BinaryIntegerHelper.Log10(unchecked((nuint)1))); + Assert.Equal(unchecked((nuint)0), BinaryIntegerHelper.Log10(unchecked((nuint)9))); + Assert.Equal(unchecked((nuint)1), BinaryIntegerHelper.Log10(unchecked((nuint)10))); + Assert.Equal(unchecked((nuint)2), BinaryIntegerHelper.Log10(unchecked((nuint)100))); + Assert.Equal(unchecked((nuint)18), BinaryIntegerHelper.Log10(unchecked((nuint)9_223_372_036_854_775_807))); + Assert.Equal(unchecked((nuint)18), BinaryIntegerHelper.Log10(unchecked((nuint)9_223_372_036_854_775_808))); + Assert.Equal(unchecked((nuint)19), BinaryIntegerHelper.Log10(unchecked((nuint)18_446_744_073_709_551_615))); + } + else + { + Assert.Equal((nuint)0, BinaryIntegerHelper.Log10((nuint)0)); + Assert.Equal((nuint)0, BinaryIntegerHelper.Log10((nuint)1)); + Assert.Equal((nuint)0, BinaryIntegerHelper.Log10((nuint)9)); + Assert.Equal((nuint)1, BinaryIntegerHelper.Log10((nuint)10)); + Assert.Equal((nuint)2, BinaryIntegerHelper.Log10((nuint)100)); + Assert.Equal((nuint)9, BinaryIntegerHelper.Log10((nuint)2_147_483_647)); + Assert.Equal((nuint)9, BinaryIntegerHelper.Log10((nuint)2_147_483_648)); + Assert.Equal((nuint)9, BinaryIntegerHelper.Log10((nuint)4_294_967_295)); } } From 1174dbc2d0577e0b0ad93ed698dc48c99cd2f599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viviana=20Due=C3=B1as=20Chavez?= Date: Thu, 2 Apr 2026 14:03:22 -0700 Subject: [PATCH 6/7] Add missing DIM tests --- .../src/System/Numerics/IBinaryInteger.cs | 6 +++-- .../src/System/Numerics/BigInteger.cs | 2 +- .../System/Numerics/DimTests.GenericMath.cs | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs index 8fde90d47b23c1..6204d6b021fcfe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs @@ -277,8 +277,10 @@ static virtual TSelf Log10(TSelf value) ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } - // Slow loop: repeatedly divide by 10 - TSelf ten = TSelf.CreateChecked(10); + // Use long literal to ensure CreateChecked resolves through + // a known conversion path (e.g., long → int) rather than + // self-to-self which lacks a TryConvertFromChecked case. + TSelf ten = TSelf.CreateChecked(10L); TSelf result = TSelf.Zero; while (value >= ten) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 05aa6f2d8153cf..5b163a0b40fcb8 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -3156,7 +3156,7 @@ static BigInteger IBinaryInteger.Log10(BigInteger value) return Zero; } - // For small values that fit in a uint, use the fast path + // For small values stored in _sign, use the fast path if (value._bits is null) { return ulong.Log10((ulong)(uint)value._sign); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs index 45c86664f6e597..c51c26a9640150 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs @@ -316,6 +316,30 @@ public static void LeadingZeroCountUInt32Test() Assert.Equal((BinaryIntegerWrapper)0x00000000, BinaryIntegerHelper>.LeadingZeroCount((uint)0xFFFFFFFF)); } + [Fact] + public static void Log10Int32Test() + { + Assert.Equal((BinaryIntegerWrapper)0, BinaryIntegerHelper>.Log10((int)0)); + Assert.Equal((BinaryIntegerWrapper)0, BinaryIntegerHelper>.Log10((int)1)); + Assert.Equal((BinaryIntegerWrapper)0, BinaryIntegerHelper>.Log10((int)9)); + Assert.Equal((BinaryIntegerWrapper)1, BinaryIntegerHelper>.Log10((int)10)); + Assert.Equal((BinaryIntegerWrapper)2, BinaryIntegerHelper>.Log10((int)100)); + Assert.Equal((BinaryIntegerWrapper)9, BinaryIntegerHelper>.Log10(int.MaxValue)); + Assert.Throws(() => BinaryIntegerHelper>.Log10(int.MinValue)); + Assert.Throws(() => BinaryIntegerHelper>.Log10((int)(-1))); + } + + [Fact] + public static void Log10UInt32Test() + { + Assert.Equal((BinaryIntegerWrapper)0, BinaryIntegerHelper>.Log10((uint)0)); + Assert.Equal((BinaryIntegerWrapper)0, BinaryIntegerHelper>.Log10((uint)1)); + Assert.Equal((BinaryIntegerWrapper)0, BinaryIntegerHelper>.Log10((uint)9)); + Assert.Equal((BinaryIntegerWrapper)1, BinaryIntegerHelper>.Log10((uint)10)); + Assert.Equal((BinaryIntegerWrapper)2, BinaryIntegerHelper>.Log10((uint)100)); + Assert.Equal((BinaryIntegerWrapper)9, BinaryIntegerHelper>.Log10(uint.MaxValue)); + } + [Fact] public static void RotateLeftInt32Test() { From 8492bdf044a1f7f18613d2acde4e0de29e28cea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viviana=20Due=C3=B1as=20Chavez?= Date: Mon, 6 Apr 2026 17:44:54 -0700 Subject: [PATCH 7/7] Add UInt128 fast path and use TARGET_64BIT for nuint. Add BigInteger higher-precision multiplier and simplified code, DIM comment update, and comprehensive power-of-10 boundary tests for all types. --- .../src/System/Numerics/IBinaryInteger.cs | 5 +-- .../src/System/UInt128.cs | 5 +++ .../src/System/UIntPtr.cs | 8 ++-- .../src/System/Numerics/BigInteger.cs | 37 +++++-------------- .../tests/BigIntegerTests.GenericMath.cs | 28 ++++++++------ .../System/CharTests.GenericMath.cs | 3 ++ .../System/Int128Tests.GenericMath.cs | 13 ++++++- .../System/Int16Tests.GenericMath.cs | 3 ++ .../System/Int32Tests.GenericMath.cs | 14 +++++++ .../System/Int64Tests.GenericMath.cs | 32 ++++++++++++++++ .../System/Numerics/DimTests.GenericMath.cs | 18 +++++++++ .../System/UInt128Tests.GenericMath.cs | 15 ++++++-- .../System/UInt16Tests.GenericMath.cs | 3 ++ .../System/UInt32Tests.GenericMath.cs | 16 +++++++- .../System/UInt64Tests.GenericMath.cs | 36 +++++++++++++++++- 15 files changed, 182 insertions(+), 54 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs index 6204d6b021fcfe..065519417526ea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs @@ -277,10 +277,7 @@ static virtual TSelf Log10(TSelf value) ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } - // Use long literal to ensure CreateChecked resolves through - // a known conversion path (e.g., long → int) rather than - // self-to-self which lacks a TryConvertFromChecked case. - TSelf ten = TSelf.CreateChecked(10L); + TSelf ten = TSelf.CreateChecked(10); TSelf result = TSelf.Zero; while (value >= ten) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index da447bda195a8f..a2be3c33114199 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -800,6 +800,11 @@ private static int LeadingZeroCountAsInt32(UInt128 value) /// public static UInt128 Log10(UInt128 value) { + if (value._upper == 0) + { + return ulong.Log10(value._lower); + } + // Approximate log10 via log2, then correct with a powers of 10 lookup table. // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 value |= 1U; diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index 6c9daa721a6084..fe1f01e314ab58 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -319,11 +319,11 @@ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatPro /// public static nuint Log10(nuint value) { - if (Environment.Is64BitProcess) - { - return (nuint)ulong.Log10((ulong)value); - } +#if TARGET_64BIT + return (nuint)ulong.Log10((ulong)value); +#else return (nuint)uint.Log10((uint)value); +#endif } /// diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 5b163a0b40fcb8..10ea7468af201e 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -3151,41 +3151,24 @@ static BigInteger IBinaryInteger.Log10(BigInteger value) ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } - if (value._sign == 0) - { - return Zero; - } - // For small values stored in _sign, use the fast path if (value._bits is null) { - return ulong.Log10((ulong)(uint)value._sign); + return uint.Log10((uint)value._sign); } - // For large values, use Log2-based estimation and correction + // For large values, use Log2-based estimation with single correction. + // log10(x) = log2(x) * log10(2); we approximate log10(2) as N/2^S. + // The smaller fixed-width types (uint, ulong, UInt128) use 1233/4096 + // (~4.6e-6 error per bit), which is sufficient for up to ~217K bits. + // For BigInteger, which has no upper bound on bit count, we use + // 1292913986/2^32 (~1.1e-10 error per bit), safe up to ~8.7B bits, + // which covers the full BigInteger range. BigInteger log2Value = Log2(value); - // log10(x) ≈ log2(x) * log10(2) ≈ log2(x) * 1233 / 4096 - BigInteger approx = (log2Value * 1233) >> 12; - - // No upper size limit, so instead of a lookup table (like with uint's) - // correct by checking against 10^approx + BigInteger approx = ((log2Value + 1) * 1292913986L) >> 32; BigInteger power = Pow(10, (int)approx); - while (value < power) - { - approx--; - power /= 10; - } - - BigInteger nextPower = power * 10; - - while (value >= nextPower) - { - approx++; - nextPower *= 10; - } - - return approx; + return value < power ? approx - 1 : approx; } /// diff --git a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs index c56dc7de68b24f..e5fb00728bffc7 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs @@ -211,11 +211,18 @@ public static void Log10Test() { Assert.Equal((BigInteger)0, BinaryIntegerHelper.Log10(Zero)); Assert.Equal((BigInteger)0, BinaryIntegerHelper.Log10(One)); - Assert.Equal((BigInteger)0, BinaryIntegerHelper.Log10((BigInteger)9)); - Assert.Equal((BigInteger)1, BinaryIntegerHelper.Log10((BigInteger)10)); - Assert.Equal((BigInteger)1, BinaryIntegerHelper.Log10((BigInteger)99)); - Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)100)); - Assert.Equal((BigInteger)2, BinaryIntegerHelper.Log10((BigInteger)999)); + + BigInteger power = 1; + for (int n = 0; n < 25; n++) + { + Assert.Equal((BigInteger)n, BinaryIntegerHelper.Log10(power)); + if (power > 1) + { + Assert.Equal((BigInteger)(n - 1), BinaryIntegerHelper.Log10(power - 1)); + } + power *= 10; + } + Assert.Equal((BigInteger)18, BinaryIntegerHelper.Log10(Int64MaxValue)); Assert.Throws(() => BinaryIntegerHelper.Log10(NegativeOne)); Assert.Throws(() => BinaryIntegerHelper.Log10(Int64MinValue)); @@ -224,9 +231,8 @@ public static void Log10Test() [Fact] public static void Log10Test_LargeValues() { - // 2^681 is the smallest power of 2 where the Log2-based approximation - // (Log2 * 1233 >> 12) undershoots by 1, exercising the correction loop. - // approx = 204, true log10 = 205 + // 2^681 produces log10 = 205, verifying correctness for values + // beyond the fixed-width type range. Assert.Equal((BigInteger)205, BinaryIntegerHelper.Log10(BigInteger.Pow(2, 681))); } @@ -234,9 +240,9 @@ public static void Log10Test_LargeValues() [OuterLoop] public static void Log10Test_VeryLargeValues() { - // 2^217769 is the smallest power of 2 where the approximation undershoots - // by 2, which a simple ±1 correction could not handle. - // approx = 65553, true log10 = 65555 + // 2^217769 produces log10 = 65555, verifying correctness for + // very large values where a less precise approximation constant + // would fail. Assert.Equal((BigInteger)65555, BinaryIntegerHelper.Log10(BigInteger.Pow(2, 217769))); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs index c2fd0c2a468bfe..c6e19a47df6872 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.GenericMath.cs @@ -186,9 +186,12 @@ public static void Log10Test() Assert.Equal((char)0, BinaryIntegerHelper.Log10((char)1)); Assert.Equal((char)0, BinaryIntegerHelper.Log10((char)9)); Assert.Equal((char)1, BinaryIntegerHelper.Log10((char)10)); + Assert.Equal((char)1, BinaryIntegerHelper.Log10((char)99)); Assert.Equal((char)2, BinaryIntegerHelper.Log10((char)100)); Assert.Equal((char)2, BinaryIntegerHelper.Log10((char)999)); + Assert.Equal((char)3, BinaryIntegerHelper.Log10((char)1000)); Assert.Equal((char)3, BinaryIntegerHelper.Log10((char)9999)); + Assert.Equal((char)4, BinaryIntegerHelper.Log10((char)10000)); Assert.Equal((char)4, BinaryIntegerHelper.Log10((char)32767)); Assert.Equal((char)4, BinaryIntegerHelper.Log10((char)32768)); Assert.Equal((char)4, BinaryIntegerHelper.Log10((char)65535)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs index 31f8bd01edaea8..e2986a79883746 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.GenericMath.cs @@ -234,7 +234,18 @@ public static void Log10Test() { Assert.Equal(0, BinaryIntegerHelper.Log10(Zero)); Assert.Equal(0, BinaryIntegerHelper.Log10(One)); - Assert.Equal(0, BinaryIntegerHelper.Log10((Int128)9)); + + Int128 power = 1; + for (int n = 0; n < 38; n++) + { + Assert.Equal((Int128)n, BinaryIntegerHelper.Log10(power)); + if (power > 1) + { + Assert.Equal((Int128)(n - 1), BinaryIntegerHelper.Log10(power - 1)); + } + power *= 10; + } + Assert.Equal(38, BinaryIntegerHelper.Log10(MaxValue)); Assert.Throws(() => BinaryIntegerHelper.Log10(MinValue)); Assert.Throws(() => BinaryIntegerHelper.Log10(NegativeOne)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs index 9026b8e11bbe5e..44f3f57461faec 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.GenericMath.cs @@ -199,9 +199,12 @@ public static void Log10Test() Assert.Equal((short)0, BinaryIntegerHelper.Log10((short)1)); Assert.Equal((short)0, BinaryIntegerHelper.Log10((short)9)); Assert.Equal((short)1, BinaryIntegerHelper.Log10((short)10)); + Assert.Equal((short)1, BinaryIntegerHelper.Log10((short)99)); Assert.Equal((short)2, BinaryIntegerHelper.Log10((short)100)); Assert.Equal((short)2, BinaryIntegerHelper.Log10((short)999)); + Assert.Equal((short)3, BinaryIntegerHelper.Log10((short)1000)); Assert.Equal((short)3, BinaryIntegerHelper.Log10((short)9999)); + Assert.Equal((short)4, BinaryIntegerHelper.Log10((short)10000)); Assert.Equal((short)4, BinaryIntegerHelper.Log10((short)32767)); Assert.Throws(() => BinaryIntegerHelper.Log10((short)(-32768))); Assert.Throws(() => BinaryIntegerHelper.Log10((short)(-1))); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs index 30fa68fcef2e05..a6166828336e85 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.GenericMath.cs @@ -199,8 +199,22 @@ public static void Log10Test() Assert.Equal(0, BinaryIntegerHelper.Log10(1)); Assert.Equal(0, BinaryIntegerHelper.Log10(9)); Assert.Equal(1, BinaryIntegerHelper.Log10(10)); + Assert.Equal(1, BinaryIntegerHelper.Log10(99)); Assert.Equal(2, BinaryIntegerHelper.Log10(100)); + Assert.Equal(2, BinaryIntegerHelper.Log10(999)); + Assert.Equal(3, BinaryIntegerHelper.Log10(1_000)); + Assert.Equal(3, BinaryIntegerHelper.Log10(9_999)); + Assert.Equal(4, BinaryIntegerHelper.Log10(10_000)); + Assert.Equal(4, BinaryIntegerHelper.Log10(99_999)); + Assert.Equal(5, BinaryIntegerHelper.Log10(100_000)); + Assert.Equal(5, BinaryIntegerHelper.Log10(999_999)); + Assert.Equal(6, BinaryIntegerHelper.Log10(1_000_000)); + Assert.Equal(6, BinaryIntegerHelper.Log10(9_999_999)); + Assert.Equal(7, BinaryIntegerHelper.Log10(10_000_000)); + Assert.Equal(7, BinaryIntegerHelper.Log10(99_999_999)); + Assert.Equal(8, BinaryIntegerHelper.Log10(100_000_000)); Assert.Equal(8, BinaryIntegerHelper.Log10(999_999_999)); + Assert.Equal(9, BinaryIntegerHelper.Log10(1_000_000_000)); Assert.Equal(9, BinaryIntegerHelper.Log10(2_147_483_647)); Assert.Throws(() => BinaryIntegerHelper.Log10(-2_147_483_648)); Assert.Throws(() => BinaryIntegerHelper.Log10(-1)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs index f9fa23322eae4c..2f2fc0098fac91 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.GenericMath.cs @@ -199,8 +199,40 @@ public static void Log10Test() Assert.Equal((long)0, BinaryIntegerHelper.Log10((long)1)); Assert.Equal((long)0, BinaryIntegerHelper.Log10((long)9)); Assert.Equal((long)1, BinaryIntegerHelper.Log10((long)10)); + Assert.Equal((long)1, BinaryIntegerHelper.Log10((long)99)); Assert.Equal((long)2, BinaryIntegerHelper.Log10((long)100)); + Assert.Equal((long)2, BinaryIntegerHelper.Log10((long)999)); + Assert.Equal((long)3, BinaryIntegerHelper.Log10((long)1_000)); + Assert.Equal((long)3, BinaryIntegerHelper.Log10((long)9_999)); + Assert.Equal((long)4, BinaryIntegerHelper.Log10((long)10_000)); + Assert.Equal((long)4, BinaryIntegerHelper.Log10((long)99_999)); + Assert.Equal((long)5, BinaryIntegerHelper.Log10((long)100_000)); + Assert.Equal((long)5, BinaryIntegerHelper.Log10((long)999_999)); + Assert.Equal((long)6, BinaryIntegerHelper.Log10((long)1_000_000)); + Assert.Equal((long)6, BinaryIntegerHelper.Log10((long)9_999_999)); + Assert.Equal((long)7, BinaryIntegerHelper.Log10((long)10_000_000)); + Assert.Equal((long)7, BinaryIntegerHelper.Log10((long)99_999_999)); + Assert.Equal((long)8, BinaryIntegerHelper.Log10((long)100_000_000)); + Assert.Equal((long)8, BinaryIntegerHelper.Log10((long)999_999_999)); + Assert.Equal((long)9, BinaryIntegerHelper.Log10((long)1_000_000_000)); + Assert.Equal((long)9, BinaryIntegerHelper.Log10((long)9_999_999_999)); + Assert.Equal((long)10, BinaryIntegerHelper.Log10((long)10_000_000_000)); + Assert.Equal((long)10, BinaryIntegerHelper.Log10((long)99_999_999_999)); + Assert.Equal((long)11, BinaryIntegerHelper.Log10((long)100_000_000_000)); + Assert.Equal((long)11, BinaryIntegerHelper.Log10((long)999_999_999_999)); + Assert.Equal((long)12, BinaryIntegerHelper.Log10((long)1_000_000_000_000)); + Assert.Equal((long)12, BinaryIntegerHelper.Log10((long)9_999_999_999_999)); + Assert.Equal((long)13, BinaryIntegerHelper.Log10((long)10_000_000_000_000)); + Assert.Equal((long)13, BinaryIntegerHelper.Log10((long)99_999_999_999_999)); + Assert.Equal((long)14, BinaryIntegerHelper.Log10((long)100_000_000_000_000)); + Assert.Equal((long)14, BinaryIntegerHelper.Log10((long)999_999_999_999_999)); + Assert.Equal((long)15, BinaryIntegerHelper.Log10((long)1_000_000_000_000_000)); + Assert.Equal((long)15, BinaryIntegerHelper.Log10((long)9_999_999_999_999_999)); + Assert.Equal((long)16, BinaryIntegerHelper.Log10((long)10_000_000_000_000_000)); + Assert.Equal((long)16, BinaryIntegerHelper.Log10((long)99_999_999_999_999_999)); + Assert.Equal((long)17, BinaryIntegerHelper.Log10((long)100_000_000_000_000_000)); Assert.Equal((long)17, BinaryIntegerHelper.Log10((long)999_999_999_999_999_999)); + Assert.Equal((long)18, BinaryIntegerHelper.Log10((long)1_000_000_000_000_000_000)); Assert.Equal((long)18, BinaryIntegerHelper.Log10((long)9_223_372_036_854_775_807)); Assert.Throws(() => BinaryIntegerHelper.Log10(-9_223_372_036_854_775_808)); Assert.Throws(() => BinaryIntegerHelper.Log10(-1)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs index c51c26a9640150..cdcf4898609f45 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs @@ -740,6 +740,12 @@ public int CompareTo(object? obj) static bool INumberBase>.TryConvertFromChecked(TOther value, out BinaryIntegerWrapper result) { + if (typeof(TOther) == typeof(T)) + { + result = (T)(object)value; + return true; + } + bool succeeded = T.TryConvertFromChecked(value, out T actualResult); if (!succeeded) @@ -752,6 +758,12 @@ static bool INumberBase>.TryConvertFromChecked(T } static bool INumberBase>.TryConvertFromSaturating(TOther value, out BinaryIntegerWrapper result) { + if (typeof(TOther) == typeof(T)) + { + result = (T)(object)value; + return true; + } + bool succeeded = T.TryConvertFromSaturating(value, out T actualResult); if (!succeeded) @@ -764,6 +776,12 @@ static bool INumberBase>.TryConvertFromSaturating>.TryConvertFromTruncating(TOther value, out BinaryIntegerWrapper result) { + if (typeof(TOther) == typeof(T)) + { + result = (T)(object)value; + return true; + } + bool succeeded = T.TryConvertFromTruncating(value, out T actualResult); if (!succeeded) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs index 906ef8b140da87..797e1e322426e7 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.GenericMath.cs @@ -235,9 +235,18 @@ public static void Log10Test() { Assert.Equal(0U, BinaryIntegerHelper.Log10(Zero)); Assert.Equal(0U, BinaryIntegerHelper.Log10(One)); - Assert.Equal(0U, BinaryIntegerHelper.Log10((UInt128)9)); - Assert.Equal(38U, BinaryIntegerHelper.Log10(Int128MaxValue)); - Assert.Equal(38U, BinaryIntegerHelper.Log10(Int128MaxValuePlusOne)); + + UInt128 power = 1; + for (uint n = 0; n < 38; n++) + { + Assert.Equal((UInt128)n, BinaryIntegerHelper.Log10(power)); + if (power > 1) + { + Assert.Equal((UInt128)(n - 1), BinaryIntegerHelper.Log10(power - 1)); + } + power *= 10; + } + Assert.Equal(38U, BinaryIntegerHelper.Log10(MaxValue)); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs index d57d63e05abcfd..4a0109d26a222b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.GenericMath.cs @@ -187,9 +187,12 @@ public static void Log10Test() Assert.Equal((ushort)0, BinaryIntegerHelper.Log10((ushort)1)); Assert.Equal((ushort)0, BinaryIntegerHelper.Log10((ushort)9)); Assert.Equal((ushort)1, BinaryIntegerHelper.Log10((ushort)10)); + Assert.Equal((ushort)1, BinaryIntegerHelper.Log10((ushort)99)); Assert.Equal((ushort)2, BinaryIntegerHelper.Log10((ushort)100)); Assert.Equal((ushort)2, BinaryIntegerHelper.Log10((ushort)999)); + Assert.Equal((ushort)3, BinaryIntegerHelper.Log10((ushort)1000)); Assert.Equal((ushort)3, BinaryIntegerHelper.Log10((ushort)9999)); + Assert.Equal((ushort)4, BinaryIntegerHelper.Log10((ushort)10000)); Assert.Equal((ushort)4, BinaryIntegerHelper.Log10((ushort)32767)); Assert.Equal((ushort)4, BinaryIntegerHelper.Log10((ushort)32768)); Assert.Equal((ushort)4, BinaryIntegerHelper.Log10((ushort)65535)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs index aa2a16fa7cd6db..25db7c87f97b85 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.GenericMath.cs @@ -188,10 +188,22 @@ public static void Log10Test() Assert.Equal((uint)0, BinaryIntegerHelper.Log10((uint)1)); Assert.Equal((uint)0, BinaryIntegerHelper.Log10((uint)9)); Assert.Equal((uint)1, BinaryIntegerHelper.Log10((uint)10)); + Assert.Equal((uint)1, BinaryIntegerHelper.Log10((uint)99)); Assert.Equal((uint)2, BinaryIntegerHelper.Log10((uint)100)); + Assert.Equal((uint)2, BinaryIntegerHelper.Log10((uint)999)); + Assert.Equal((uint)3, BinaryIntegerHelper.Log10((uint)1_000)); + Assert.Equal((uint)3, BinaryIntegerHelper.Log10((uint)9_999)); + Assert.Equal((uint)4, BinaryIntegerHelper.Log10((uint)10_000)); + Assert.Equal((uint)4, BinaryIntegerHelper.Log10((uint)99_999)); + Assert.Equal((uint)5, BinaryIntegerHelper.Log10((uint)100_000)); + Assert.Equal((uint)5, BinaryIntegerHelper.Log10((uint)999_999)); + Assert.Equal((uint)6, BinaryIntegerHelper.Log10((uint)1_000_000)); + Assert.Equal((uint)6, BinaryIntegerHelper.Log10((uint)9_999_999)); + Assert.Equal((uint)7, BinaryIntegerHelper.Log10((uint)10_000_000)); + Assert.Equal((uint)7, BinaryIntegerHelper.Log10((uint)99_999_999)); + Assert.Equal((uint)8, BinaryIntegerHelper.Log10((uint)100_000_000)); Assert.Equal((uint)8, BinaryIntegerHelper.Log10((uint)999_999_999)); - Assert.Equal((uint)9, BinaryIntegerHelper.Log10((uint)2_147_483_647)); - Assert.Equal((uint)9, BinaryIntegerHelper.Log10((uint)2_147_483_648)); + Assert.Equal((uint)9, BinaryIntegerHelper.Log10((uint)1_000_000_000)); Assert.Equal((uint)9, BinaryIntegerHelper.Log10((uint)4_294_967_295)); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs index 37291ccead42d3..5c2c77fd52fd13 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.GenericMath.cs @@ -195,10 +195,42 @@ public static void Log10Test() Assert.Equal((ulong)0, BinaryIntegerHelper.Log10((ulong)1)); Assert.Equal((ulong)0, BinaryIntegerHelper.Log10((ulong)9)); Assert.Equal((ulong)1, BinaryIntegerHelper.Log10((ulong)10)); + Assert.Equal((ulong)1, BinaryIntegerHelper.Log10((ulong)99)); Assert.Equal((ulong)2, BinaryIntegerHelper.Log10((ulong)100)); + Assert.Equal((ulong)2, BinaryIntegerHelper.Log10((ulong)999)); + Assert.Equal((ulong)3, BinaryIntegerHelper.Log10((ulong)1_000)); + Assert.Equal((ulong)3, BinaryIntegerHelper.Log10((ulong)9_999)); + Assert.Equal((ulong)4, BinaryIntegerHelper.Log10((ulong)10_000)); + Assert.Equal((ulong)4, BinaryIntegerHelper.Log10((ulong)99_999)); + Assert.Equal((ulong)5, BinaryIntegerHelper.Log10((ulong)100_000)); + Assert.Equal((ulong)5, BinaryIntegerHelper.Log10((ulong)999_999)); + Assert.Equal((ulong)6, BinaryIntegerHelper.Log10((ulong)1_000_000)); + Assert.Equal((ulong)6, BinaryIntegerHelper.Log10((ulong)9_999_999)); + Assert.Equal((ulong)7, BinaryIntegerHelper.Log10((ulong)10_000_000)); + Assert.Equal((ulong)7, BinaryIntegerHelper.Log10((ulong)99_999_999)); + Assert.Equal((ulong)8, BinaryIntegerHelper.Log10((ulong)100_000_000)); + Assert.Equal((ulong)8, BinaryIntegerHelper.Log10((ulong)999_999_999)); + Assert.Equal((ulong)9, BinaryIntegerHelper.Log10((ulong)1_000_000_000)); + Assert.Equal((ulong)9, BinaryIntegerHelper.Log10((ulong)9_999_999_999)); + Assert.Equal((ulong)10, BinaryIntegerHelper.Log10((ulong)10_000_000_000)); + Assert.Equal((ulong)10, BinaryIntegerHelper.Log10((ulong)99_999_999_999)); + Assert.Equal((ulong)11, BinaryIntegerHelper.Log10((ulong)100_000_000_000)); + Assert.Equal((ulong)11, BinaryIntegerHelper.Log10((ulong)999_999_999_999)); + Assert.Equal((ulong)12, BinaryIntegerHelper.Log10((ulong)1_000_000_000_000)); + Assert.Equal((ulong)12, BinaryIntegerHelper.Log10((ulong)9_999_999_999_999)); + Assert.Equal((ulong)13, BinaryIntegerHelper.Log10((ulong)10_000_000_000_000)); + Assert.Equal((ulong)13, BinaryIntegerHelper.Log10((ulong)99_999_999_999_999)); + Assert.Equal((ulong)14, BinaryIntegerHelper.Log10((ulong)100_000_000_000_000)); + Assert.Equal((ulong)14, BinaryIntegerHelper.Log10((ulong)999_999_999_999_999)); + Assert.Equal((ulong)15, BinaryIntegerHelper.Log10((ulong)1_000_000_000_000_000)); + Assert.Equal((ulong)15, BinaryIntegerHelper.Log10((ulong)9_999_999_999_999_999)); + Assert.Equal((ulong)16, BinaryIntegerHelper.Log10((ulong)10_000_000_000_000_000)); + Assert.Equal((ulong)16, BinaryIntegerHelper.Log10((ulong)99_999_999_999_999_999)); + Assert.Equal((ulong)17, BinaryIntegerHelper.Log10((ulong)100_000_000_000_000_000)); + Assert.Equal((ulong)17, BinaryIntegerHelper.Log10((ulong)999_999_999_999_999_999)); + Assert.Equal((ulong)18, BinaryIntegerHelper.Log10((ulong)1_000_000_000_000_000_000)); Assert.Equal((ulong)18, BinaryIntegerHelper.Log10((ulong)9_999_999_999_999_999_999)); - Assert.Equal((ulong)18, BinaryIntegerHelper.Log10((ulong)9_223_372_036_854_775_807)); - Assert.Equal((ulong)18, BinaryIntegerHelper.Log10((ulong)9_223_372_036_854_775_808)); + Assert.Equal((ulong)19, BinaryIntegerHelper.Log10((ulong)10_000_000_000_000_000_000)); Assert.Equal((ulong)19, BinaryIntegerHelper.Log10((ulong)18_446_744_073_709_551_615)); }