1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Diagnostics;
6 using System.Globalization;
7 using System.Runtime.InteropServices;
11 // The Parse methods provided by the numeric classes convert a
12 // string to a numeric value. The optional style parameter specifies the
13 // permitted style of the numeric string. It must be a combination of bit flags
14 // from the NumberStyles enumeration. The optional info parameter
15 // specifies the NumberFormatInfo instance to use when parsing the
16 // string. If the info parameter is null or omitted, the numeric
17 // formatting information is obtained from the current culture.
19 // Numeric strings produced by the Format methods using the Currency,
20 // Decimal, Engineering, Fixed point, General, or Number standard formats
21 // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable
22 // by the Parse methods if the NumberStyles.Any style is
23 // specified. Note, however, that the Parse methods do not accept
24 // NaNs or Infinities.
26 internal partial class Number
28 private const int Int32Precision = 10;
29 private const int UInt32Precision = Int32Precision;
30 private const int Int64Precision = 19;
31 private const int UInt64Precision = 20;
33 private static bool HexNumberToInt32(ref NumberBuffer number, ref int value)
36 bool returnValue = HexNumberToUInt32(ref number, ref passedValue);
37 value = (int)passedValue;
41 private static bool HexNumberToInt64(ref NumberBuffer number, ref long value)
43 ulong passedValue = 0;
44 bool returnValue = HexNumberToUInt64(ref number, ref passedValue);
45 value = (long)passedValue;
49 private static unsafe bool HexNumberToUInt32(ref NumberBuffer number, ref uint value)
52 if (i > UInt32Precision || i < number.precision)
56 char* p = number.digits;
57 Debug.Assert(p != null);
62 if (n > ((uint)0xFFFFFFFF / 16))
72 if (*p >= '0' && *p <= '9')
74 newN += (uint)(*p - '0');
78 if (*p >= 'A' && *p <= 'F')
80 newN += (uint)((*p - 'A') + 10);
84 Debug.Assert(*p >= 'a' && *p <= 'f');
85 newN += (uint)((*p - 'a') + 10);
91 // Detect an overflow here...
103 private static unsafe bool HexNumberToUInt64(ref NumberBuffer number, ref ulong value)
105 int i = number.scale;
106 if (i > UInt64Precision || i < number.precision)
110 char* p = number.digits;
111 Debug.Assert(p != null);
116 if (n > (0xFFFFFFFFFFFFFFFF / 16))
126 if (*p >= '0' && *p <= '9')
128 newN += (ulong)(*p - '0');
132 if (*p >= 'A' && *p <= 'F')
134 newN += (ulong)((*p - 'A') + 10);
138 Debug.Assert(*p >= 'a' && *p <= 'f');
139 newN += (ulong)((*p - 'a') + 10);
145 // Detect an overflow here...
157 private static unsafe bool NumberToInt32(ref NumberBuffer number, ref int value)
159 int i = number.scale;
160 if (i > Int32Precision || i < number.precision)
164 char* p = number.digits;
165 Debug.Assert(p != null);
169 if ((uint)n > (0x7FFFFFFF / 10))
176 n += (int)(*p++ - '0');
198 private static unsafe bool NumberToInt64(ref NumberBuffer number, ref long value)
200 int i = number.scale;
201 if (i > Int64Precision || i < number.precision)
205 char* p = number.digits;
206 Debug.Assert(p != null);
210 if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10))
217 n += (int)(*p++ - '0');
239 private static unsafe bool NumberToUInt32(ref NumberBuffer number, ref uint value)
241 int i = number.scale;
242 if (i > UInt32Precision || i < number.precision || number.sign)
246 char* p = number.digits;
247 Debug.Assert(p != null);
251 if (n > (0xFFFFFFFF / 10))
258 uint newN = n + (uint)(*p++ - '0');
259 // Detect an overflow here...
271 private static unsafe bool NumberToUInt64(ref NumberBuffer number, ref ulong value)
273 int i = number.scale;
274 if (i > UInt64Precision || i < number.precision || number.sign)
278 char* p = number.digits;
279 Debug.Assert(p != null);
283 if (n > (0xFFFFFFFFFFFFFFFF / 10))
290 ulong newN = n + (ulong)(*p++ - '0');
291 // Detect an overflow here...
303 internal unsafe static int ParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
305 NumberBuffer number = default;
308 StringToNumber(s, style, ref number, info, false);
310 if ((style & NumberStyles.AllowHexSpecifier) != 0)
312 if (!HexNumberToInt32(ref number, ref i))
314 throw new OverflowException(SR.Overflow_Int32);
319 if (!NumberToInt32(ref number, ref i))
321 throw new OverflowException(SR.Overflow_Int32);
327 internal unsafe static long ParseInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
329 NumberBuffer number = default;
332 StringToNumber(value, options, ref number, numfmt, false);
334 if ((options & NumberStyles.AllowHexSpecifier) != 0)
336 if (!HexNumberToInt64(ref number, ref i))
338 throw new OverflowException(SR.Overflow_Int64);
343 if (!NumberToInt64(ref number, ref i))
345 throw new OverflowException(SR.Overflow_Int64);
351 internal unsafe static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
353 NumberBuffer number = default;
356 StringToNumber(value, options, ref number, numfmt, false);
358 if ((options & NumberStyles.AllowHexSpecifier) != 0)
360 if (!HexNumberToUInt32(ref number, ref i))
362 throw new OverflowException(SR.Overflow_UInt32);
367 if (!NumberToUInt32(ref number, ref i))
369 throw new OverflowException(SR.Overflow_UInt32);
376 internal unsafe static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
378 NumberBuffer number = default;
381 StringToNumber(value, options, ref number, numfmt, false);
382 if ((options & NumberStyles.AllowHexSpecifier) != 0)
384 if (!HexNumberToUInt64(ref number, ref i))
386 throw new OverflowException(SR.Overflow_UInt64);
391 if (!NumberToUInt64(ref number, ref i))
393 throw new OverflowException(SR.Overflow_UInt64);
399 private unsafe static bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
401 const int StateSign = 0x0001;
402 const int StateParens = 0x0002;
403 const int StateDigits = 0x0004;
404 const int StateNonZero = 0x0008;
405 const int StateDecimal = 0x0010;
406 const int StateCurrency = 0x0020;
410 string decSep; // decimal separator from NumberFormatInfo.
411 string groupSep; // group separator from NumberFormatInfo.
412 string currSymbol = null; // currency symbol from NumberFormatInfo.
414 bool parsingCurrency = false;
415 if ((options & NumberStyles.AllowCurrencySymbol) != 0)
417 currSymbol = numfmt.CurrencySymbol;
419 // The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast.
420 // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
421 decSep = numfmt.CurrencyDecimalSeparator;
422 groupSep = numfmt.CurrencyGroupSeparator;
423 parsingCurrency = true;
427 decSep = numfmt.NumberDecimalSeparator;
428 groupSep = numfmt.NumberGroupSeparator;
438 // Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
439 // "-Kr 1231.47" is legal but "- 1231.47" is not.
440 if (!IsWhite(ch) || (options & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2)))
442 if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true))))
447 else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
449 state |= StateSign | StateParens;
452 else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null)
454 state |= StateCurrency;
456 // We already found the currency symbol. There should not be more currency symbols. Set
457 // currSymbol to NULL so that we won't search it again in the later code path.
471 if ((ch >= '0' && ch <= '9') || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))))
473 state |= StateDigits;
475 if (ch != '0' || (state & StateNonZero) != 0)
477 if (digCount < NumberMaxDigits)
479 number.digits[digCount++] = ch;
480 if (ch != '0' || parseDecimal)
485 if ((state & StateDecimal) == 0)
489 state |= StateNonZero;
491 else if ((state & StateDecimal) != 0)
496 else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberDecimalSeparator)) != null))
498 state |= StateDecimal;
501 else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberGroupSeparator)) != null))
513 number.precision = digEnd;
514 number.digits[digEnd] = '\0';
515 if ((state & StateDigits) != 0)
517 if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0))
521 if ((next = MatchChars(p, numfmt.positiveSign)) != null)
525 else if ((next = MatchChars(p, numfmt.negativeSign)) != null)
530 if (ch >= '0' && ch <= '9')
535 exp = exp * 10 + (ch - '0');
540 while (ch >= '0' && ch <= '9')
545 } while (ch >= '0' && ch <= '9');
560 if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0)
562 if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true))))
567 else if (ch == ')' && ((state & StateParens) != 0))
569 state &= ~StateParens;
571 else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null)
583 if ((state & StateParens) == 0)
585 if ((state & StateNonZero) == 0)
591 if ((state & StateDecimal) == 0)
604 internal unsafe static bool TryParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out int result)
606 NumberBuffer number = default;
609 if (!TryStringToNumber(s, style, ref number, info, false))
614 if ((style & NumberStyles.AllowHexSpecifier) != 0)
616 if (!HexNumberToInt32(ref number, ref result))
623 if (!NumberToInt32(ref number, ref result))
631 internal unsafe static bool TryParseInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out long result)
633 NumberBuffer number = default;
636 if (!TryStringToNumber(s, style, ref number, info, false))
641 if ((style & NumberStyles.AllowHexSpecifier) != 0)
643 if (!HexNumberToInt64(ref number, ref result))
650 if (!NumberToInt64(ref number, ref result))
658 internal unsafe static bool TryParseUInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out uint result)
660 NumberBuffer number = default;
663 if (!TryStringToNumber(s, style, ref number, info, false))
668 if ((style & NumberStyles.AllowHexSpecifier) != 0)
670 if (!HexNumberToUInt32(ref number, ref result))
677 if (!NumberToUInt32(ref number, ref result))
685 internal unsafe static bool TryParseUInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out ulong result)
687 NumberBuffer number = default;
690 if (!TryStringToNumber(s, style, ref number, info, false))
695 if ((style & NumberStyles.AllowHexSpecifier) != 0)
697 if (!HexNumberToUInt64(ref number, ref result))
704 if (!NumberToUInt64(ref number, ref result))
712 internal unsafe static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
714 NumberBuffer number = default;
717 StringToNumber(value, options, ref number, numfmt, true);
719 if (!NumberBufferToDecimal(ref number, ref result))
721 throw new OverflowException(SR.Overflow_Decimal);
726 internal unsafe static double ParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
728 NumberBuffer number = default;
731 if (!TryStringToNumber(value, options, ref number, numfmt, false))
733 //If we failed TryStringToNumber, it may be from one of our special strings.
734 //Check the three with which we're concerned and rethrow if it's not one of
736 ReadOnlySpan<char> sTrim = value.Trim();
737 if (sTrim.EqualsOrdinal(numfmt.PositiveInfinitySymbol))
739 return double.PositiveInfinity;
741 if (sTrim.EqualsOrdinal(numfmt.NegativeInfinitySymbol))
743 return double.NegativeInfinity;
745 if (sTrim.EqualsOrdinal(numfmt.NaNSymbol))
749 throw new FormatException(SR.Format_InvalidString);
752 if (!NumberBufferToDouble(ref number, ref d))
754 throw new OverflowException(SR.Overflow_Double);
760 internal unsafe static float ParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
762 NumberBuffer number = default;
765 if (!TryStringToNumber(value, options, ref number, numfmt, false))
767 //If we failed TryStringToNumber, it may be from one of our special strings.
768 //Check the three with which we're concerned and rethrow if it's not one of
770 ReadOnlySpan<char> sTrim = value.Trim();
771 if (sTrim.EqualsOrdinal(numfmt.PositiveInfinitySymbol))
773 return float.PositiveInfinity;
775 if (sTrim.EqualsOrdinal(numfmt.NegativeInfinitySymbol))
777 return float.NegativeInfinity;
779 if (sTrim.EqualsOrdinal(numfmt.NaNSymbol))
783 throw new FormatException(SR.Format_InvalidString);
786 if (!NumberBufferToDouble(ref number, ref d))
788 throw new OverflowException(SR.Overflow_Single);
790 float castSingle = (float)d;
791 if (float.IsInfinity(castSingle))
793 throw new OverflowException(SR.Overflow_Single);
798 internal unsafe static bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out decimal result)
800 NumberBuffer number = default;
803 if (!TryStringToNumber(value, options, ref number, numfmt, true))
808 if (!NumberBufferToDecimal(ref number, ref result))
815 internal unsafe static bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out double result)
817 NumberBuffer number = default;
821 if (!TryStringToNumber(value, options, ref number, numfmt, false))
825 if (!NumberBufferToDouble(ref number, ref result))
832 internal unsafe static bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out float result)
834 NumberBuffer number = default;
838 if (!TryStringToNumber(value, options, ref number, numfmt, false))
842 if (!NumberBufferToDouble(ref number, ref d))
846 float castSingle = (float)d;
847 if (float.IsInfinity(castSingle))
856 private static unsafe void StringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
858 Debug.Assert(info != null);
859 fixed (char* stringPointer = &MemoryMarshal.GetReference(str))
861 char* p = stringPointer;
862 if (!ParseNumber(ref p, options, ref number, info, parseDecimal)
863 || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
865 throw new FormatException(SR.Format_InvalidString);
870 internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
872 Debug.Assert(numfmt != null);
873 fixed (char* stringPointer = &MemoryMarshal.GetReference(str))
875 char* p = stringPointer;
876 if (!ParseNumber(ref p, options, ref number, numfmt, parseDecimal)
877 || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
886 private static bool TrailingZeros(ReadOnlySpan<char> s, int index)
888 // For compatibility, we need to allow trailing zeros at the end of a number string
889 for (int i = index; i < s.Length; i++)
900 private unsafe static char* MatchChars(char* p, string str)
902 fixed (char* stringPointer = str)
904 return MatchChars(p, stringPointer);
908 private unsafe static char* MatchChars(char* p, char* str)
910 Debug.Assert(p != null && str != null);
917 // We only hurt the failure case
918 // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
919 // space character we use 0x20 space character instead to mean the same.
920 while (*p == *str || (*str == '\u00a0' && *p == '\u0020'))
924 if (*str == '\0') return p;
930 private static bool IsWhite(char ch) => ch == 0x20 || (ch >= 0x09 && ch <= 0x0D);
932 private static bool NumberBufferToDouble(ref NumberBuffer number, ref double value)
934 double d = NumberToDouble(ref number);
935 uint e = DoubleHelper.Exponent(d);
936 ulong m = DoubleHelper.Mantissa(d);
943 if (e == 0 && m == 0)
952 private static class DoubleHelper
954 public static unsafe uint Exponent(double d) =>
955 (*((uint*)&d + 1) >> 20) & 0x000007ff;
957 public static unsafe ulong Mantissa(double d) =>
958 *((ulong*)&d) & 0x000fffffffffffff;
960 public static unsafe bool Sign(double d) =>
961 (*((uint*)&d + 1) >> 31) != 0;