From 1c1f006330bc972c59305444f33804714b18b23d Mon Sep 17 00:00:00 2001 From: lilinus Date: Sat, 27 Apr 2024 14:40:38 +0200 Subject: [PATCH 01/11] Refactor some DateTime and TimeSpan formatting/parsing methods --- .../System/Globalization/DateTimeFormat.cs | 8 ++-- .../src/System/Globalization/DateTimeParse.cs | 1 + .../System/Globalization/TimeSpanFormat.cs | 6 +-- .../src/System/Globalization/TimeSpanParse.cs | 38 +++++++++++-------- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index 895a9e83b6a89d..3bc1298527f74e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -484,11 +484,11 @@ private static void FormatCustomized( tokenLen = ParseRepeatPattern(format, i, ch); if (tokenLen <= MaxSecondsFractionDigits) { - long fraction = (dateTime.Ticks % Calendar.TicksPerSecond); - fraction /= (long)Math.Pow(10, 7 - tokenLen); + int fraction = (int)(dateTime.Ticks % Calendar.TicksPerSecond); + fraction /= TimeSpanParse.Pow10(MaxSecondsFractionDigits - tokenLen); if (ch == 'f') { - FormatFraction(ref result, (int)fraction, fixedNumberFormats[tokenLen - 1]); + FormatFraction(ref result, fraction, fixedNumberFormats[tokenLen - 1]); } else { @@ -507,7 +507,7 @@ private static void FormatCustomized( } if (effectiveDigits > 0) { - FormatFraction(ref result, (int)fraction, fixedNumberFormats[effectiveDigits - 1]); + FormatFraction(ref result, fraction, fixedNumberFormats[effectiveDigits - 1]); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index 687cafbe9ca500..1cf93ce07006c4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -3179,6 +3179,7 @@ internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDig private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, scoped ref double result) { + Debug.Assert(maxDigitLen <= DateTimeFormat.MaxSecondsFractionDigits); if (!str.GetNextDigit()) { str.Index--; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs index 8c8bea12bbf6b2..e7f9f791694368 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs @@ -311,7 +311,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< int seconds = (int)(time / TimeSpan.TicksPerSecond % 60); int fraction = (int)(time % TimeSpan.TicksPerSecond); - long tmp; + int tmp; int i = 0; int tokenLen; @@ -357,7 +357,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< tmp = fraction; tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); - DateTimeFormat.FormatFraction(ref result, (int)tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1]); + DateTimeFormat.FormatFraction(ref result, tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1]); break; case 'F': // @@ -386,7 +386,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< } if (effectiveDigits > 0) { - DateTimeFormat.FormatFraction(ref result, (int)tmp, DateTimeFormat.fixedNumberFormats[effectiveDigits - 1]); + DateTimeFormat.FormatFraction(ref result, tmp, DateTimeFormat.fixedNumberFormats[effectiveDigits - 1]); } break; case 'd': diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index c900e64dcb9696..2cff1f046c7c9a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -47,7 +47,10 @@ // //////////////////////////////////////////////////////////////////////////// +using System.Buffers.Text; using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; namespace System.Globalization @@ -114,7 +117,7 @@ public bool NormalizeAndValidateFraction() if (_zeroes == 0 && _num > MaxFraction) return false; - int totalDigitsCount = ((int)Math.Floor(Math.Log10(_num))) + 1 + _zeroes; + int totalDigitsCount = FormattingHelpers.CountDigits((uint)_num) + _zeroes; if (totalDigitsCount == MaxFractionDigits) { @@ -132,7 +135,7 @@ public bool NormalizeAndValidateFraction() // .000001 normalize to 10 ticks // .1 normalize to 1,000,000 ticks - _num *= (int)Pow10(MaxFractionDigits - totalDigitsCount); + _num *= Pow10(MaxFractionDigits - totalDigitsCount); return true; } @@ -143,7 +146,7 @@ public bool NormalizeAndValidateFraction() // .099999999 normalize to 1,000,000 ticks Debug.Assert(_zeroes > 0); // Already validated that in the condition _zeroes == 0 && _num > MaxFraction - _num = (int)Math.Round((double)_num / Pow10(totalDigitsCount - MaxFractionDigits), MidpointRounding.AwayFromZero); + _num = (int)Math.Round(_num / Math.Pow(10, totalDigitsCount - MaxFractionDigits), MidpointRounding.AwayFromZero); Debug.Assert(_num < MaxFraction); return true; @@ -563,20 +566,23 @@ internal bool SetBadFormatSpecifierFailure(char? formatSpecifierCharacter = null } } - internal static long Pow10(int pow) + internal static int Pow10(int pow) { - return pow switch - { - 0 => 1, - 1 => 10, - 2 => 100, - 3 => 1000, - 4 => 10000, - 5 => 100000, - 6 => 1000000, - 7 => 10000000, - _ => (long)Math.Pow(10, pow), - }; + Debug.Assert(pow > 0); + Debug.Assert(pow <= MaxFractionDigits); + + ReadOnlySpan powersOfTen = [ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + ]; + Debug.Assert(powersOfTen.Length == MaxFractionDigits + 1); + return Unsafe.Add(ref MemoryMarshal.GetReference(powersOfTen), pow); } private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result) From 001ee0a0dd75fb553ed2b7ce7f80adf1b172a53a Mon Sep 17 00:00:00 2001 From: lilinus Date: Sat, 27 Apr 2024 14:42:43 +0200 Subject: [PATCH 02/11] Fix assertion in TimeSpanParse.Pow10 --- .../src/System/Globalization/TimeSpanParse.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index 2cff1f046c7c9a..f1413c00ffa17a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -568,9 +568,8 @@ internal bool SetBadFormatSpecifierFailure(char? formatSpecifierCharacter = null internal static int Pow10(int pow) { - Debug.Assert(pow > 0); + Debug.Assert(pow >= 0); Debug.Assert(pow <= MaxFractionDigits); - ReadOnlySpan powersOfTen = [ 1, 10, From decf099d1eaf4a34146207b2a26047f44e78d664 Mon Sep 17 00:00:00 2001 From: lilinus Date: Sat, 27 Apr 2024 15:25:11 +0200 Subject: [PATCH 03/11] Don't use Unsafe in TimeSpanParse.Pow10 --- .../src/System/Globalization/TimeSpanParse.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index f1413c00ffa17a..2f20b14c4f1468 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -49,8 +49,6 @@ using System.Buffers.Text; using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text; namespace System.Globalization @@ -581,7 +579,7 @@ internal static int Pow10(int pow) 10000000, ]; Debug.Assert(powersOfTen.Length == MaxFractionDigits + 1); - return Unsafe.Add(ref MemoryMarshal.GetReference(powersOfTen), pow); + return powersOfTen[pow]; } private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result) From 267d5e861c022c338de5004bb6c85e26d6179039 Mon Sep 17 00:00:00 2001 From: lilinus Date: Sat, 27 Apr 2024 15:34:51 +0200 Subject: [PATCH 04/11] Revert changes to TimeSpanParse.Pow10 --- .../System/Globalization/DateTimeFormat.cs | 2 +- .../System/Globalization/TimeSpanFormat.cs | 4 +-- .../src/System/Globalization/TimeSpanParse.cs | 30 +++++++++---------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index 3bc1298527f74e..02ee787c5f21c1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -485,7 +485,7 @@ private static void FormatCustomized( if (tokenLen <= MaxSecondsFractionDigits) { int fraction = (int)(dateTime.Ticks % Calendar.TicksPerSecond); - fraction /= TimeSpanParse.Pow10(MaxSecondsFractionDigits - tokenLen); + fraction /= (int)TimeSpanParse.Pow10(MaxSecondsFractionDigits - tokenLen); if (ch == 'f') { FormatFraction(ref result, fraction, fixedNumberFormats[tokenLen - 1]); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs index e7f9f791694368..6f468ce5a92f4f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs @@ -356,7 +356,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< } tmp = fraction; - tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + tmp /= (int)TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); DateTimeFormat.FormatFraction(ref result, tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1]); break; case 'F': @@ -370,7 +370,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< } tmp = fraction; - tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + tmp /= (int)TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); int effectiveDigits = tokenLen; while (effectiveDigits > 0) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index 2f20b14c4f1468..a48f423058fa4f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -133,7 +133,7 @@ public bool NormalizeAndValidateFraction() // .000001 normalize to 10 ticks // .1 normalize to 1,000,000 ticks - _num *= Pow10(MaxFractionDigits - totalDigitsCount); + _num *= (int)Pow10(MaxFractionDigits - totalDigitsCount); return true; } @@ -564,22 +564,20 @@ internal bool SetBadFormatSpecifierFailure(char? formatSpecifierCharacter = null } } - internal static int Pow10(int pow) + internal static long Pow10(int pow) { - Debug.Assert(pow >= 0); - Debug.Assert(pow <= MaxFractionDigits); - ReadOnlySpan powersOfTen = [ - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - ]; - Debug.Assert(powersOfTen.Length == MaxFractionDigits + 1); - return powersOfTen[pow]; + return pow switch + { + 0 => 1, + 1 => 10, + 2 => 100, + 3 => 1000, + 4 => 10000, + 5 => 100000, + 6 => 1000000, + 7 => 10000000, + _ => (long)Math.Pow(10, pow), + }; } private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result) From 61863e304562402b41e9b19bd35222700956473b Mon Sep 17 00:00:00 2001 From: lilinus Date: Sat, 27 Apr 2024 16:04:14 +0200 Subject: [PATCH 05/11] Revert "Revert changes to TimeSpanParse.Pow10" This reverts commit 267d5e861c022c338de5004bb6c85e26d6179039. --- .../System/Globalization/DateTimeFormat.cs | 2 +- .../System/Globalization/TimeSpanFormat.cs | 4 +-- .../src/System/Globalization/TimeSpanParse.cs | 30 ++++++++++--------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index 02ee787c5f21c1..3bc1298527f74e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -485,7 +485,7 @@ private static void FormatCustomized( if (tokenLen <= MaxSecondsFractionDigits) { int fraction = (int)(dateTime.Ticks % Calendar.TicksPerSecond); - fraction /= (int)TimeSpanParse.Pow10(MaxSecondsFractionDigits - tokenLen); + fraction /= TimeSpanParse.Pow10(MaxSecondsFractionDigits - tokenLen); if (ch == 'f') { FormatFraction(ref result, fraction, fixedNumberFormats[tokenLen - 1]); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs index 6f468ce5a92f4f..e7f9f791694368 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs @@ -356,7 +356,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< } tmp = fraction; - tmp /= (int)TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); DateTimeFormat.FormatFraction(ref result, tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1]); break; case 'F': @@ -370,7 +370,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< } tmp = fraction; - tmp /= (int)TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); int effectiveDigits = tokenLen; while (effectiveDigits > 0) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index a48f423058fa4f..2f20b14c4f1468 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -133,7 +133,7 @@ public bool NormalizeAndValidateFraction() // .000001 normalize to 10 ticks // .1 normalize to 1,000,000 ticks - _num *= (int)Pow10(MaxFractionDigits - totalDigitsCount); + _num *= Pow10(MaxFractionDigits - totalDigitsCount); return true; } @@ -564,20 +564,22 @@ internal bool SetBadFormatSpecifierFailure(char? formatSpecifierCharacter = null } } - internal static long Pow10(int pow) + internal static int Pow10(int pow) { - return pow switch - { - 0 => 1, - 1 => 10, - 2 => 100, - 3 => 1000, - 4 => 10000, - 5 => 100000, - 6 => 1000000, - 7 => 10000000, - _ => (long)Math.Pow(10, pow), - }; + Debug.Assert(pow >= 0); + Debug.Assert(pow <= MaxFractionDigits); + ReadOnlySpan powersOfTen = [ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + ]; + Debug.Assert(powersOfTen.Length == MaxFractionDigits + 1); + return powersOfTen[pow]; } private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result) From 95857be015688fbbe498b5fb7db24010f125cf74 Mon Sep 17 00:00:00 2001 From: lilinus Date: Sat, 27 Apr 2024 20:42:37 +0200 Subject: [PATCH 06/11] Change method name to Pow10UpToMaxFractionDigits --- .../src/System/Globalization/DateTimeFormat.cs | 2 +- .../src/System/Globalization/DateTimeParse.cs | 2 +- .../src/System/Globalization/TimeSpanFormat.cs | 4 ++-- .../src/System/Globalization/TimeSpanParse.cs | 6 ++---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index 3bc1298527f74e..db12f89bf2dd18 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -485,7 +485,7 @@ private static void FormatCustomized( if (tokenLen <= MaxSecondsFractionDigits) { int fraction = (int)(dateTime.Ticks % Calendar.TicksPerSecond); - fraction /= TimeSpanParse.Pow10(MaxSecondsFractionDigits - tokenLen); + fraction /= TimeSpanParse.Pow10UpToMaxFractionDigits(MaxSecondsFractionDigits - tokenLen); if (ch == 'f') { FormatFraction(ref result, fraction, fixedNumberFormats[tokenLen - 1]); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index 1cf93ce07006c4..9613bddf435490 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -3198,7 +3198,7 @@ private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, scop result = result * 10 + str.GetDigit(); } - result /= TimeSpanParse.Pow10(digitLen); + result /= TimeSpanParse.Pow10UpToMaxFractionDigits(digitLen); return digitLen == maxDigitLen; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs index e7f9f791694368..48faa5b1a98345 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs @@ -356,7 +356,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< } tmp = fraction; - tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + tmp /= TimeSpanParse.Pow10UpToMaxFractionDigits(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); DateTimeFormat.FormatFraction(ref result, tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1]); break; case 'F': @@ -370,7 +370,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< } tmp = fraction; - tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + tmp /= TimeSpanParse.Pow10UpToMaxFractionDigits(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); int effectiveDigits = tokenLen; while (effectiveDigits > 0) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index 2f20b14c4f1468..6e256815a77074 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -133,7 +133,7 @@ public bool NormalizeAndValidateFraction() // .000001 normalize to 10 ticks // .1 normalize to 1,000,000 ticks - _num *= Pow10(MaxFractionDigits - totalDigitsCount); + _num *= Pow10UpToMaxFractionDigits(MaxFractionDigits - totalDigitsCount); return true; } @@ -564,10 +564,8 @@ internal bool SetBadFormatSpecifierFailure(char? formatSpecifierCharacter = null } } - internal static int Pow10(int pow) + internal static int Pow10UpToMaxFractionDigits(int pow) { - Debug.Assert(pow >= 0); - Debug.Assert(pow <= MaxFractionDigits); ReadOnlySpan powersOfTen = [ 1, 10, From 2fe27c1536c06e5ec001f030a67ed6cbbdd2f032 Mon Sep 17 00:00:00 2001 From: lilinus Date: Sun, 28 Apr 2024 12:15:54 +0200 Subject: [PATCH 07/11] Fix TimeSpanParse.TimeSpanToken.NormalizeAndValidateFraction --- .../src/System/Globalization/TimeSpanParse.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index 6e256815a77074..99632b6cb10bb2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -144,7 +144,17 @@ public bool NormalizeAndValidateFraction() // .099999999 normalize to 1,000,000 ticks Debug.Assert(_zeroes > 0); // Already validated that in the condition _zeroes == 0 && _num > MaxFraction - _num = (int)Math.Round(_num / Math.Pow(10, totalDigitsCount - MaxFractionDigits), MidpointRounding.AwayFromZero); + + if (_zeroes > MaxFractionDigits) + { + // If there are 8 leading zeroes, it rounds to zero + _num = 0; + return true; + } + + Debug.Assert(totalDigitsCount - MaxFractionDigits <= MaxFractionDigits); + uint power = (uint)Pow10UpToMaxFractionDigits(totalDigitsCount - MaxFractionDigits); + _num = (int)(((uint)_num + power / 2) / power); Debug.Assert(_num < MaxFraction); return true; From dd64b4259ffbde4ed806ba1767a87dfcb8ea9e2e Mon Sep 17 00:00:00 2001 From: lilinus Date: Thu, 2 May 2024 15:36:51 +0200 Subject: [PATCH 08/11] Address feedback in TimeSpanParse --- .../src/System/Globalization/TimeSpanParse.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index 99632b6cb10bb2..40c114ba7a9c3f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -153,8 +153,7 @@ public bool NormalizeAndValidateFraction() } Debug.Assert(totalDigitsCount - MaxFractionDigits <= MaxFractionDigits); - uint power = (uint)Pow10UpToMaxFractionDigits(totalDigitsCount - MaxFractionDigits); - _num = (int)(((uint)_num + power / 2) / power); + _num = (int)Math.Round((double)_num / Pow10UpToMaxFractionDigits(totalDigitsCount - MaxFractionDigits), MidpointRounding.AwayFromZero); Debug.Assert(_num < MaxFraction); return true; @@ -576,7 +575,8 @@ internal bool SetBadFormatSpecifierFailure(char? formatSpecifierCharacter = null internal static int Pow10UpToMaxFractionDigits(int pow) { - ReadOnlySpan powersOfTen = [ + ReadOnlySpan powersOfTen = + [ 1, 10, 100, From 0c04b2ff4afa68578339fe2bb5ae9a4a22483eaf Mon Sep 17 00:00:00 2001 From: lilinus Date: Fri, 3 May 2024 10:51:21 +0200 Subject: [PATCH 09/11] Change from Math.Round to uint divison in TimeSpanParse.NormalizeAndValidateFraction --- .../src/System/Globalization/TimeSpanParse.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index 40c114ba7a9c3f..b41b8d1b01fdf8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -153,7 +153,8 @@ public bool NormalizeAndValidateFraction() } Debug.Assert(totalDigitsCount - MaxFractionDigits <= MaxFractionDigits); - _num = (int)Math.Round((double)_num / Pow10UpToMaxFractionDigits(totalDigitsCount - MaxFractionDigits), MidpointRounding.AwayFromZero); + uint power = (uint)Pow10UpToMaxFractionDigits(totalDigitsCount - MaxFractionDigits); + _num = (int)(((uint)_num + power / 2) / power); Debug.Assert(_num < MaxFraction); return true; From e45515014f478cddd01e9097e204e733eb85ec4a Mon Sep 17 00:00:00 2001 From: lilinus Date: Sun, 5 May 2024 12:00:07 +0200 Subject: [PATCH 10/11] Comment for rounding division in TimeSpanParse.NormalizeAndValidateFraction --- .../src/System/Globalization/TimeSpanParse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index b41b8d1b01fdf8..3d5a2570d3c21f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -154,7 +154,7 @@ public bool NormalizeAndValidateFraction() Debug.Assert(totalDigitsCount - MaxFractionDigits <= MaxFractionDigits); uint power = (uint)Pow10UpToMaxFractionDigits(totalDigitsCount - MaxFractionDigits); - _num = (int)(((uint)_num + power / 2) / power); + _num = (int)(((uint)_num + power / 2) / power); // Rounding integer divsion, (midpint rounds up). Debug.Assert(_num < MaxFraction); return true; From ef86e5a392bd1cd466f24451c811b744f5cd617a Mon Sep 17 00:00:00 2001 From: Linus Hamlin <78953007+lilinus@users.noreply.github.com> Date: Sun, 5 May 2024 22:35:51 +0200 Subject: [PATCH 11/11] Update src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs Co-authored-by: xtqqczze <45661989+xtqqczze@users.noreply.github.com> --- .../src/System/Globalization/TimeSpanParse.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs index 3d5a2570d3c21f..033d0abe4e9049 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs @@ -154,7 +154,8 @@ public bool NormalizeAndValidateFraction() Debug.Assert(totalDigitsCount - MaxFractionDigits <= MaxFractionDigits); uint power = (uint)Pow10UpToMaxFractionDigits(totalDigitsCount - MaxFractionDigits); - _num = (int)(((uint)_num + power / 2) / power); // Rounding integer divsion, (midpint rounds up). + // Unsigned integer division, rounding away from zero + _num = (int)(((uint)_num + power / 2) / power); Debug.Assert(_num < MaxFraction); return true;