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;
8 using Internal.Runtime.CompilerServices;
12 // The Parse methods provided by the numeric classes convert a
13 // string to a numeric value. The optional style parameter specifies the
14 // permitted style of the numeric string. It must be a combination of bit flags
15 // from the NumberStyles enumeration. The optional info parameter
16 // specifies the NumberFormatInfo instance to use when parsing the
17 // string. If the info parameter is null or omitted, the numeric
18 // formatting information is obtained from the current culture.
20 // Numeric strings produced by the Format methods using the Currency,
21 // Decimal, Engineering, Fixed point, General, or Number standard formats
22 // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable
23 // by the Parse methods if the NumberStyles.Any style is
24 // specified. Note, however, that the Parse methods do not accept
25 // NaNs or Infinities.
27 internal partial class Number
29 private const int Int32Precision = 10;
30 private const int UInt32Precision = Int32Precision;
31 private const int Int64Precision = 19;
32 private const int UInt64Precision = 20;
34 /// <summary>256-element map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
35 internal static readonly int[] s_charToHexLookup =
37 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
38 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
39 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
40 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
41 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
42 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
43 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111
44 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127
45 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143
46 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159
47 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175
48 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191
49 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207
50 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223
51 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239
52 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 255
55 private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value)
57 number.CheckConsistency();
60 if (i > Int32Precision || i < number.DigitsCount)
64 byte* p = number.GetDigitsPointer();
65 Debug.Assert(p != null);
69 if ((uint)n > (0x7FFFFFFF / 10))
79 if (number.IsNegative)
98 private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long value)
100 number.CheckConsistency();
102 int i = number.Scale;
103 if (i > Int64Precision || i < number.DigitsCount)
107 byte* p = number.GetDigitsPointer();
108 Debug.Assert(p != null);
112 if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10))
122 if (number.IsNegative)
141 private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value)
143 number.CheckConsistency();
145 int i = number.Scale;
146 if (i > UInt32Precision || i < number.DigitsCount || number.IsNegative)
150 byte* p = number.GetDigitsPointer();
151 Debug.Assert(p != null);
155 if (n > (0xFFFFFFFF / 10))
162 uint newN = n + (uint)(*p++ - '0');
163 // Detect an overflow here...
175 private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong value)
177 number.CheckConsistency();
179 int i = number.Scale;
180 if (i > UInt64Precision || i < number.DigitsCount || number.IsNegative)
184 byte* p = number.GetDigitsPointer();
185 Debug.Assert(p != null);
189 if (n > (0xFFFFFFFFFFFFFFFF / 10))
196 ulong newN = n + (ulong)(*p++ - '0');
197 // Detect an overflow here...
209 internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
211 if (!TryParseInt32(value, styles, info, out int result, out bool failureIsOverflow))
213 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int32));
219 internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
221 if (!TryParseInt64(value, styles, info, out long result, out bool failureIsOverflow))
223 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int64));
229 internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
231 if (!TryParseUInt32(value, styles, info, out uint result, out bool failureIsOverflow))
233 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt32));
239 internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
241 if (!TryParseUInt64(value, styles, info, out ulong result, out bool failureIsOverflow))
243 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt64));
249 private static unsafe bool TryParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
251 Debug.Assert(str != null);
252 Debug.Assert(strEnd != null);
253 Debug.Assert(str <= strEnd);
254 Debug.Assert((styles & NumberStyles.AllowHexSpecifier) == 0);
256 const int StateSign = 0x0001;
257 const int StateParens = 0x0002;
258 const int StateDigits = 0x0004;
259 const int StateNonZero = 0x0008;
260 const int StateDecimal = 0x0010;
261 const int StateCurrency = 0x0020;
263 Debug.Assert(number.DigitsCount == 0);
264 Debug.Assert(number.Scale == 0);
265 Debug.Assert(number.IsNegative == false);
266 Debug.Assert(number.HasNonZeroTail == false);
268 number.CheckConsistency();
270 string decSep; // decimal separator from NumberFormatInfo.
271 string groupSep; // group separator from NumberFormatInfo.
272 string currSymbol = null; // currency symbol from NumberFormatInfo.
274 bool parsingCurrency = false;
275 if ((styles & NumberStyles.AllowCurrencySymbol) != 0)
277 currSymbol = info.CurrencySymbol;
279 // 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.
280 // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
281 decSep = info.CurrencyDecimalSeparator;
282 groupSep = info.CurrencyGroupSeparator;
283 parsingCurrency = true;
287 decSep = info.NumberDecimalSeparator;
288 groupSep = info.NumberGroupSeparator;
293 char ch = p < strEnd ? *p : '\0';
298 // Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
299 // "-Kr 1231.47" is legal but "- 1231.47" is not.
300 if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
302 if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchChars(p, strEnd, info.NegativeSign)) != null && (number.IsNegative = true))))
307 else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
309 state |= StateSign | StateParens;
310 number.IsNegative = true;
312 else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
314 state |= StateCurrency;
316 // We already found the currency symbol. There should not be more currency symbols. Set
317 // currSymbol to NULL so that we won't search it again in the later code path.
325 ch = ++p < strEnd ? *p : '\0';
330 int maxDigCount = number.Digits.Length - 1;
336 state |= StateDigits;
338 if (ch != '0' || (state & StateNonZero) != 0)
340 if (digCount < maxDigCount)
342 number.Digits[digCount++] = (byte)(ch);
343 if ((ch != '0') || (number.Kind != NumberBufferKind.Integer))
350 // For decimal and binary floating-point numbers, we only
351 // need to store digits up to maxDigCount. However, we still
352 // need to keep track of whether any additional digits past
353 // maxDigCount were non-zero, as that can impact rounding
354 // for an input that falls evenly between two representable
357 number.HasNonZeroTail = true;
360 if ((state & StateDecimal) == 0)
364 state |= StateNonZero;
366 else if ((state & StateDecimal) != 0)
371 else if (((styles & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberDecimalSeparator)) != null))
373 state |= StateDecimal;
376 else if (((styles & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberGroupSeparator)) != null))
384 ch = ++p < strEnd ? *p : '\0';
388 number.DigitsCount = digEnd;
389 number.Digits[digEnd] = (byte)('\0');
390 if ((state & StateDigits) != 0)
392 if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
395 ch = ++p < strEnd ? *p : '\0';
396 if ((next = MatchChars(p, strEnd, info.positiveSign)) != null)
398 ch = (p = next) < strEnd ? *p : '\0';
400 else if ((next = MatchChars(p, strEnd, info.negativeSign)) != null)
402 ch = (p = next) < strEnd ? *p : '\0';
410 exp = exp * 10 + (ch - '0');
411 ch = ++p < strEnd ? *p : '\0';
417 ch = ++p < strEnd ? *p : '\0';
420 } while (IsDigit(ch));
430 ch = p < strEnd ? *p : '\0';
435 if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
437 if (((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchChars(p, strEnd, info.NegativeSign)) != null) && (number.IsNegative = true))))
442 else if (ch == ')' && ((state & StateParens) != 0))
444 state &= ~StateParens;
446 else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
456 ch = ++p < strEnd ? *p : '\0';
458 if ((state & StateParens) == 0)
460 if ((state & StateNonZero) == 0)
462 if (number.Kind != NumberBufferKind.Decimal)
466 if ((number.Kind == NumberBufferKind.Integer) && (state & StateDecimal) == 0)
468 number.IsNegative = false;
479 internal static unsafe bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, out bool failureIsOverflow)
482 failureIsOverflow = false;
484 if ((styles & ~NumberStyles.Integer) == 0)
486 // Optimized path for the common case of anything that's allowed for integer style.
487 return TryParseInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
490 if ((styles & NumberStyles.AllowHexSpecifier) != 0)
492 return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result), ref failureIsOverflow);
495 byte* pDigits = stackalloc byte[Int32NumberBufferLength];
496 NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
498 if (!TryStringToNumber(value, styles, ref number, info))
503 if (!TryNumberToInt32(ref number, ref result))
505 failureIsOverflow = true;
512 /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
513 private static bool TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, ref bool failureIsOverflow)
515 Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
516 Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
518 if ((uint)value.Length < 1)
521 bool overflow = false;
526 // Skip past any whitespace at the beginning.
527 if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
532 if ((uint)index >= (uint)value.Length)
536 while (IsWhite(num));
539 // Parse leading sign.
540 if ((styles & NumberStyles.AllowLeadingSign) != 0)
542 string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
544 if (positiveSign == "+" && negativeSign == "-")
550 if ((uint)index >= (uint)value.Length)
557 if ((uint)index >= (uint)value.Length)
564 value = value.Slice(index);
566 if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
568 index += positiveSign.Length;
569 if ((uint)index >= (uint)value.Length)
573 else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
576 index += negativeSign.Length;
577 if ((uint)index >= (uint)value.Length)
588 // Skip past leading zeros.
594 if ((uint)index >= (uint)value.Length)
595 goto DoneAtEndButPotentialOverflow;
597 } while (num == '0');
599 goto HasTrailingChars;
602 // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
603 answer = num - '0'; // first digit
605 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
607 if ((uint)index >= (uint)value.Length)
608 goto DoneAtEndButPotentialOverflow;
611 goto HasTrailingChars;
613 answer = 10 * answer + num - '0';
616 // Potential overflow now processing the 10th digit.
617 if ((uint)index >= (uint)value.Length)
618 goto DoneAtEndButPotentialOverflow;
621 goto HasTrailingChars;
623 if (answer > int.MaxValue / 10)
627 answer = answer * 10 + num - '0';
628 if ((uint)answer > (uint)int.MaxValue + (-1 * sign + 1) / 2)
632 if ((uint)index >= (uint)value.Length)
633 goto DoneAtEndButPotentialOverflow;
635 // At this point, we're either overflowing or hitting a formatting error.
636 // Format errors take precedence for compatibility.
642 if ((uint)index >= (uint)value.Length)
643 goto DoneAtEndButPotentialOverflow;
646 goto HasTrailingChars;
649 FalseExit: // parsing failed
653 DoneAtEndButPotentialOverflow:
656 failureIsOverflow = true;
659 result = answer * sign;
662 HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
663 // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
666 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
668 for (index++; index < value.Length; index++)
670 if (!IsWhite(value[index]))
673 if ((uint)index >= (uint)value.Length)
674 goto DoneAtEndButPotentialOverflow;
677 if (!TrailingZeros(value, index))
680 goto DoneAtEndButPotentialOverflow;
683 /// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
684 private static bool TryParseInt64IntegerStyle(
685 ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, ref bool failureIsOverflow)
687 Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
688 Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
690 if ((uint)value.Length < 1)
693 bool overflow = false;
698 // Skip past any whitespace at the beginning.
699 if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
704 if ((uint)index >= (uint)value.Length)
708 while (IsWhite(num));
711 // Parse leading sign.
712 if ((styles & NumberStyles.AllowLeadingSign) != 0)
714 string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
716 if (positiveSign == "+" && negativeSign == "-")
722 if ((uint)index >= (uint)value.Length)
729 if ((uint)index >= (uint)value.Length)
736 value = value.Slice(index);
738 if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
740 index += positiveSign.Length;
741 if ((uint)index >= (uint)value.Length)
745 else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
748 index += negativeSign.Length;
749 if ((uint)index >= (uint)value.Length)
760 // Skip past leading zeros.
766 if ((uint)index >= (uint)value.Length)
767 goto DoneAtEndButPotentialOverflow;
769 } while (num == '0');
771 goto HasTrailingChars;
774 // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
775 answer = num - '0'; // first digit
777 for (int i = 0; i < 17; i++) // next 17 digits can't overflow
779 if ((uint)index >= (uint)value.Length)
780 goto DoneAtEndButPotentialOverflow;
783 goto HasTrailingChars;
785 answer = 10 * answer + num - '0';
788 // Potential overflow now processing the 19th digit.
789 if ((uint)index >= (uint)value.Length)
790 goto DoneAtEndButPotentialOverflow;
793 goto HasTrailingChars;
795 if (answer > long.MaxValue / 10)
799 answer = answer * 10 + num - '0';
800 if ((ulong)answer > (ulong)long.MaxValue + (ulong)((-1 * sign + 1) / 2)) // + sign => 0, - sign => 1
804 if ((uint)index >= (uint)value.Length)
805 goto DoneAtEndButPotentialOverflow;
807 // At this point, we're either overflowing or hitting a formatting error.
808 // Format errors take precedence for compatibility.
814 if ((uint)index >= (uint)value.Length)
815 goto DoneAtEndButPotentialOverflow;
818 goto HasTrailingChars;
821 FalseExit: // parsing failed
825 DoneAtEndButPotentialOverflow:
828 failureIsOverflow = true;
831 result = answer * sign;
834 HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
835 // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
838 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
840 for (index++; index < value.Length; index++)
842 if (!IsWhite(value[index]))
845 if ((uint)index >= (uint)value.Length)
846 goto DoneAtEndButPotentialOverflow;
849 if (!TrailingZeros(value, index))
852 goto DoneAtEndButPotentialOverflow;
855 internal static unsafe bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, out bool failureIsOverflow)
858 failureIsOverflow = false;
860 if ((styles & ~NumberStyles.Integer) == 0)
862 // Optimized path for the common case of anything that's allowed for integer style.
863 return TryParseInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
866 if ((styles & NumberStyles.AllowHexSpecifier) != 0)
868 return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result), ref failureIsOverflow);
871 byte* pDigits = stackalloc byte[Int64NumberBufferLength];
872 NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
874 if (!TryStringToNumber(value, styles, ref number, info))
879 if (!TryNumberToInt64(ref number, ref result))
881 failureIsOverflow = true;
888 internal static unsafe bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, out bool failureIsOverflow)
891 failureIsOverflow = false;
893 if ((styles & ~NumberStyles.Integer) == 0)
895 // Optimized path for the common case of anything that's allowed for integer style.
896 return TryParseUInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
899 if ((styles & NumberStyles.AllowHexSpecifier) != 0)
901 return TryParseUInt32HexNumberStyle(value, styles, out result, ref failureIsOverflow);
904 byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
905 NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
907 if (!TryStringToNumber(value, styles, ref number, info))
912 if (!TryNumberToUInt32(ref number, ref result))
914 failureIsOverflow = true;
921 /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
922 private static bool TryParseUInt32IntegerStyle(
923 ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
925 Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
926 Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
928 if ((uint)value.Length < 1)
931 bool overflow = false;
932 bool hasNegativeSign = false;
936 // Skip past any whitespace at the beginning.
937 if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
942 if ((uint)index >= (uint)value.Length)
946 while (IsWhite(num));
949 // Parse leading sign.
950 if ((styles & NumberStyles.AllowLeadingSign) != 0)
952 string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
954 if (positiveSign == "+" && negativeSign == "-")
959 if ((uint)index >= (uint)value.Length)
965 hasNegativeSign = true;
967 if ((uint)index >= (uint)value.Length)
974 value = value.Slice(index);
976 if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
978 index += positiveSign.Length;
979 if ((uint)index >= (uint)value.Length)
983 else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
985 hasNegativeSign = true;
986 index += negativeSign.Length;
987 if ((uint)index >= (uint)value.Length)
998 // Skip past leading zeros.
1004 if ((uint)index >= (uint)value.Length)
1005 goto DoneAtEndButPotentialOverflow;
1007 } while (num == '0');
1009 goto HasTrailingChars;
1012 // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
1013 answer = num - '0'; // first digit
1015 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
1017 if ((uint)index >= (uint)value.Length)
1018 goto DoneAtEndButPotentialOverflow;
1021 goto HasTrailingChars;
1023 answer = 10 * answer + num - '0';
1026 // Potential overflow now processing the 10th digit.
1027 if ((uint)index >= (uint)value.Length)
1028 goto DoneAtEndButPotentialOverflow;
1031 goto HasTrailingChars;
1033 if ((uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5'))
1037 answer = answer * 10 + num - '0';
1038 if ((uint)index >= (uint)value.Length)
1039 goto DoneAtEndButPotentialOverflow;
1041 // At this point, we're either overflowing or hitting a formatting error.
1042 // Format errors take precedence for compatibility.
1044 while (IsDigit(num))
1048 if ((uint)index >= (uint)value.Length)
1049 goto DoneAtEndButPotentialOverflow;
1052 goto HasTrailingChars;
1055 FalseExit: // parsing failed
1059 DoneAtEndButPotentialOverflow:
1060 if (overflow || (hasNegativeSign && answer != 0))
1062 failureIsOverflow = true;
1065 result = (uint)answer;
1068 HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
1069 // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
1072 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
1074 for (index++; index < value.Length; index++)
1076 if (!IsWhite(value[index]))
1079 if ((uint)index >= (uint)value.Length)
1080 goto DoneAtEndButPotentialOverflow;
1083 if (!TrailingZeros(value, index))
1086 goto DoneAtEndButPotentialOverflow;
1089 /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
1090 private static bool TryParseUInt32HexNumberStyle(
1091 ReadOnlySpan<char> value, NumberStyles styles, out uint result, ref bool failureIsOverflow)
1093 Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
1094 Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
1096 if ((uint)value.Length < 1)
1099 bool overflow = false;
1104 // Skip past any whitespace at the beginning.
1105 if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
1110 if ((uint)index >= (uint)value.Length)
1114 while (IsWhite(num));
1118 int[] charToHexLookup = s_charToHexLookup;
1120 if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
1122 // Skip past leading zeros.
1128 if ((uint)index >= (uint)value.Length)
1131 } while (num == '0');
1132 if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
1133 goto HasTrailingChars;
1136 // Parse up through 8 digits, as no overflow is possible
1137 answer = charToHexLookup[num]; // first digit
1139 for (int i = 0; i < 7; i++) // next 7 digits can't overflow
1141 if ((uint)index >= (uint)value.Length)
1144 if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
1145 goto HasTrailingChars;
1147 answer = 16 * answer + numValue;
1150 // If there's another digit, it's an overflow.
1151 if ((uint)index >= (uint)value.Length)
1154 if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
1155 goto HasTrailingChars;
1158 if ((uint)index >= (uint)value.Length)
1159 goto DoneAtEndButPotentialOverflow;
1161 // At this point, we're either overflowing or hitting a formatting error.
1162 // Format errors take precedence for compatibility. Read through any remaining digits.
1164 while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
1167 if ((uint)index >= (uint)value.Length)
1168 goto DoneAtEndButPotentialOverflow;
1171 goto HasTrailingChars;
1174 FalseExit: // parsing failed
1178 DoneAtEndButPotentialOverflow:
1181 failureIsOverflow = true;
1185 result = (uint)answer;
1188 HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
1189 // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
1192 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
1194 for (index++; index < value.Length; index++)
1196 if (!IsWhite(value[index]))
1199 if ((uint)index >= (uint)value.Length)
1200 goto DoneAtEndButPotentialOverflow;
1203 if (!TrailingZeros(value, index))
1206 goto DoneAtEndButPotentialOverflow;
1209 internal static unsafe bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, out bool failureIsOverflow)
1212 failureIsOverflow = false;
1214 if ((styles & ~NumberStyles.Integer) == 0)
1216 // Optimized path for the common case of anything that's allowed for integer style.
1217 return TryParseUInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
1220 if ((styles & NumberStyles.AllowHexSpecifier) != 0)
1222 return TryParseUInt64HexNumberStyle(value, styles, out result, ref failureIsOverflow);
1225 byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
1226 NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
1228 if (!TryStringToNumber(value, styles, ref number, info))
1233 if (!TryNumberToUInt64(ref number, ref result))
1235 failureIsOverflow = true;
1242 /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
1243 private static bool TryParseUInt64IntegerStyle(
1244 ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
1246 Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
1247 Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
1249 if ((uint)value.Length < 1)
1252 bool overflow = false;
1253 bool hasNegativeSign = false;
1257 // Skip past any whitespace at the beginning.
1258 if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
1263 if ((uint)index >= (uint)value.Length)
1267 while (IsWhite(num));
1270 // Parse leading sign.
1271 if ((styles & NumberStyles.AllowLeadingSign) != 0)
1273 string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
1275 if (positiveSign == "+" && negativeSign == "-")
1280 if ((uint)index >= (uint)value.Length)
1284 else if (num == '-')
1286 hasNegativeSign = true;
1288 if ((uint)index >= (uint)value.Length)
1295 value = value.Slice(index);
1297 if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
1299 index += positiveSign.Length;
1300 if ((uint)index >= (uint)value.Length)
1304 else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
1306 hasNegativeSign = true;
1307 index += negativeSign.Length;
1308 if ((uint)index >= (uint)value.Length)
1319 // Skip past leading zeros.
1325 if ((uint)index >= (uint)value.Length)
1326 goto DoneAtEndButPotentialOverflow;
1328 } while (num == '0');
1330 goto HasTrailingChars;
1333 // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
1334 answer = num - '0'; // first digit
1336 for (int i = 0; i < 18; i++) // next 18 digits can't overflow
1338 if ((uint)index >= (uint)value.Length)
1339 goto DoneAtEndButPotentialOverflow;
1342 goto HasTrailingChars;
1344 answer = 10 * answer + num - '0';
1347 // Potential overflow now processing the 20th digit.
1348 if ((uint)index >= (uint)value.Length)
1349 goto DoneAtEndButPotentialOverflow;
1352 goto HasTrailingChars;
1354 if ((ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5'))
1358 answer = answer * 10 + num - '0';
1359 if ((uint)index >= (uint)value.Length)
1360 goto DoneAtEndButPotentialOverflow;
1362 // At this point, we're either overflowing or hitting a formatting error.
1363 // Format errors take precedence for compatibility.
1365 while (IsDigit(num))
1369 if ((uint)index >= (uint)value.Length)
1370 goto DoneAtEndButPotentialOverflow;
1373 goto HasTrailingChars;
1376 FalseExit: // parsing failed
1380 DoneAtEndButPotentialOverflow:
1381 if (overflow || (hasNegativeSign && answer != 0))
1383 failureIsOverflow = true;
1386 result = (ulong)answer;
1389 HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
1390 // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
1393 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
1395 for (index++; index < value.Length; index++)
1397 if (!IsWhite(value[index]))
1400 if ((uint)index >= (uint)value.Length)
1401 goto DoneAtEndButPotentialOverflow;
1404 if (!TrailingZeros(value, index))
1407 goto DoneAtEndButPotentialOverflow;
1410 /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
1411 private static bool TryParseUInt64HexNumberStyle(
1412 ReadOnlySpan<char> value, NumberStyles styles, out ulong result, ref bool failureIsOverflow)
1414 Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
1415 Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
1417 if ((uint)value.Length < 1)
1420 bool overflow = false;
1425 // Skip past any whitespace at the beginning.
1426 if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
1431 if ((uint)index >= (uint)value.Length)
1435 while (IsWhite(num));
1439 int[] charToHexLookup = s_charToHexLookup;
1441 if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
1443 // Skip past leading zeros.
1449 if ((uint)index >= (uint)value.Length)
1452 } while (num == '0');
1453 if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
1454 goto HasTrailingChars;
1457 // Parse up through 16 digits, as no overflow is possible
1458 answer = charToHexLookup[num]; // first digit
1460 for (int i = 0; i < 15; i++) // next 15 digits can't overflow
1462 if ((uint)index >= (uint)value.Length)
1465 if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
1466 goto HasTrailingChars;
1468 answer = 16 * answer + numValue;
1471 // If there's another digit, it's an overflow.
1472 if ((uint)index >= (uint)value.Length)
1475 if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
1476 goto HasTrailingChars;
1479 if ((uint)index >= (uint)value.Length)
1480 goto DoneAtEndButPotentialOverflow;
1482 // At this point, we're either overflowing or hitting a formatting error.
1483 // Format errors take precedence for compatibility. Read through any remaining digits.
1485 while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
1488 if ((uint)index >= (uint)value.Length)
1489 goto DoneAtEndButPotentialOverflow;
1492 goto HasTrailingChars;
1495 FalseExit: // parsing failed
1499 DoneAtEndButPotentialOverflow:
1502 failureIsOverflow = true;
1506 result = (ulong)answer;
1509 HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
1510 // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
1513 if ((styles & NumberStyles.AllowTrailingWhite) == 0)
1515 for (index++; index < value.Length; index++)
1517 if (!IsWhite(value[index]))
1520 if ((uint)index >= (uint)value.Length)
1521 goto DoneAtEndButPotentialOverflow;
1524 if (!TrailingZeros(value, index))
1527 goto DoneAtEndButPotentialOverflow;
1530 internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
1532 if (!TryParseDecimal(value, styles, info, out decimal result, out bool failureIsOverflow))
1534 ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Decimal));
1540 internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
1542 number.CheckConsistency();
1544 byte* p = number.GetDigitsPointer();
1545 int e = number.Scale;
1546 bool sign = number.IsNegative;
1550 // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force
1551 // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.)
1552 value = new decimal(0, 0, 0, sign, (byte)Math.Clamp(-e, 0, 28));
1556 if (e > DecimalPrecision)
1566 if (low64 >= ulong.MaxValue / 10)
1574 if (low64 >= ulong.MaxValue / 10)
1582 while ((e > 0 || (c != 0 && e > -28)) &&
1583 (high < uint.MaxValue / 10 || (high == uint.MaxValue / 10 && (low64 < 0x99999999_99999999 || (low64 == 0x99999999_99999999 && c <= '5')))))
1586 ulong tmpLow = (uint)low64 * 10UL;
1587 ulong tmp64 = (uint)(low64 >> 32) * 10UL + (tmpLow >> 32);
1588 low64 = (uint)tmpLow + (tmp64 << 32);
1589 high = (uint)(tmp64 >> 32) + high * 10;
1604 if ((c == '5') && ((low64 & 1) == 0))
1608 // At this point we should either be at the end of the buffer, or just
1609 // have a single rounding digit left, and the next should be the end
1610 Debug.Assert((c == 0) || (p[1] == 0));
1612 if (((c == 0) || c == '0') && !number.HasNonZeroTail)
1614 // When the next digit is 5, the number is even, and all following digits are zero
1615 // we don't need to round.
1620 if (++low64 == 0 && ++high == 0)
1622 low64 = 0x99999999_9999999A;
1623 high = uint.MaxValue / 10;
1632 if (e <= -DecimalPrecision)
1634 // Parsing a large scale zero can give you more precision than fits in the decimal.
1635 // This should only happen for actual zeros or very small numbers that round to zero.
1636 value = new decimal(0, 0, 0, sign, DecimalPrecision - 1);
1640 value = new decimal((int)low64, (int)(low64 >> 32), (int)high, sign, (byte)-e);
1645 internal static double ParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
1647 if (!TryParseDouble(value, styles, info, out double result))
1649 ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
1655 internal static float ParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
1657 if (!TryParseSingle(value, styles, info, out float result))
1659 ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
1665 internal static unsafe bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result, out bool failureIsOverflow)
1667 byte* pDigits = stackalloc byte[DecimalNumberBufferLength];
1668 NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
1671 failureIsOverflow = false;
1673 if (!TryStringToNumber(value, styles, ref number, info))
1678 if (!TryNumberToDecimal(ref number, ref result))
1680 failureIsOverflow = true;
1687 internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
1689 byte* pDigits = stackalloc byte[DoubleNumberBufferLength];
1690 NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, DoubleNumberBufferLength);
1692 if (!TryStringToNumber(value, styles, ref number, info))
1694 ReadOnlySpan<char> valueTrim = value.Trim();
1696 // This code would be simpler if we only had the concept of `InfinitySymbol`, but
1697 // we don't so we'll check the existing cases first and then handle `PositiveSign` +
1698 // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
1700 if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
1702 result = double.PositiveInfinity;
1704 else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
1706 result = double.NegativeInfinity;
1708 else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
1710 result = double.NaN;
1712 else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
1714 valueTrim = valueTrim.Slice(info.PositiveSign.Length);
1716 if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
1718 result = double.PositiveInfinity;
1720 else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
1722 result = double.NaN;
1730 else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
1731 valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
1733 result = double.NaN;
1738 return false; // We really failed
1743 result = NumberToDouble(ref number);
1749 internal static unsafe bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result)
1751 byte* pDigits = stackalloc byte[SingleNumberBufferLength];
1752 NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, SingleNumberBufferLength);
1754 if (!TryStringToNumber(value, styles, ref number, info))
1756 ReadOnlySpan<char> valueTrim = value.Trim();
1758 // This code would be simpler if we only had the concept of `InfinitySymbol`, but
1759 // we don't so we'll check the existing cases first and then handle `PositiveSign` +
1760 // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
1762 // Additionally, since some cultures ("wo") actually define `PositiveInfinitySymbol`
1763 // to include `PositiveSign`, we need to check whether `PositiveInfinitySymbol` fits
1764 // that case so that we don't start parsing things like `++infini`.
1766 if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
1768 result = float.PositiveInfinity;
1770 else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
1772 result = float.NegativeInfinity;
1774 else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
1778 else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
1780 valueTrim = valueTrim.Slice(info.PositiveSign.Length);
1782 if (!info.PositiveInfinitySymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
1784 result = float.PositiveInfinity;
1786 else if (!info.NaNSymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
1796 else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
1797 !info.NaNSymbol.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
1798 valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
1805 return false; // We really failed
1810 result = NumberToSingle(ref number);
1816 private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
1818 if (!TryStringToNumber(value, styles, ref number, info))
1820 ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
1824 internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
1826 Debug.Assert(info != null);
1827 fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
1829 char* p = stringPointer;
1830 if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info)
1831 || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
1833 number.CheckConsistency();
1838 number.CheckConsistency();
1842 private static bool TrailingZeros(ReadOnlySpan<char> value, int index)
1844 // For compatibility, we need to allow trailing zeros at the end of a number string
1845 for (int i = index; i < value.Length; i++)
1847 if (value[i] != '\0')
1856 private static unsafe char* MatchChars(char* p, char* pEnd, string value)
1858 Debug.Assert(p != null && pEnd != null && p <= pEnd && value != null);
1859 fixed (char* stringPointer = value)
1861 char* str = stringPointer;
1864 // We only hurt the failure case
1865 // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
1866 // space character we use 0x20 space character instead to mean the same.
1869 char cp = p < pEnd ? *p : '\0';
1870 if (cp != *str && !(*str == '\u00a0' && cp == '\u0020'))
1885 private static bool IsWhite(int ch) => ch == 0x20 || ((uint)(ch - 0x09) <= (0x0D - 0x09));
1887 private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;
1889 private static void ThrowOverflowOrFormatException(bool overflow, string overflowResourceKey)
1892 new OverflowException(SR.GetResourceString(overflowResourceKey)) :
1893 (Exception)new FormatException(SR.Format_InvalidString);
1896 internal static double NumberToDouble(ref NumberBuffer number)
1898 number.CheckConsistency();
1900 ulong bits = NumberToFloatingPointBits(ref number, in FloatingPointInfo.Double);
1901 double result = BitConverter.Int64BitsToDouble((long)(bits));
1902 return number.IsNegative ? -result : result;
1905 internal static float NumberToSingle(ref NumberBuffer number)
1907 number.CheckConsistency();
1909 uint bits = (uint)(NumberToFloatingPointBits(ref number, in FloatingPointInfo.Single));
1910 float result = BitConverter.Int32BitsToSingle((int)(bits));
1911 return number.IsNegative ? -result : result;