private static int SkipExtraDigits(ref UString s, int radix, ParseNumberFlag flags) { for (int skipped = 0;; skipped++, s = s.Slice(1)) { char c = s[0, '\0']; uint digit = (uint)Base36DigitValue(c); if (digit >= radix) { if ((c == ' ' || c == '\t') && (flags & ParseNumberFlag.SkipSpacesInsideNumber) != 0) { continue; } else if (c == '_' && (flags & ParseNumberFlag.SkipUnderscores) != 0) { continue; } else if (c == '\'' && (flags & ParseNumberFlag.SkipSingleQuotes) != 0) { continue; } else { return(skipped); } } } }
static bool TryParseUInt(ref UString s, ref ulong result, int radix, ParseNumberFlag flags, out int numDigits) { numDigits = 0; if ((flags & ParseNumberFlag.SkipSpacesInFront) != 0) { s = SkipSpaces(s); } bool overflow = false; for (;; s = s.Slice(1)) { char c = s[0, '\0']; uint digit = (uint)Base36DigitValue(c); if (digit >= radix) { if ((c == ' ' || c == '\t') && (flags & ParseNumberFlag.SkipSpacesInsideNumber) != 0) { continue; } else if (c == '_' && (flags & ParseNumberFlag.SkipUnderscores) != 0) { continue; } else if (c == '\'' && (flags & ParseNumberFlag.SkipSingleQuotes) != 0) { continue; } else { break; } } ulong next; try { next = checked (result * (uint)radix + digit); } catch (OverflowException) { overflow = true; if ((flags & ParseNumberFlag.StopBeforeOverflow) != 0) { return(false); } next = result * (uint)radix + digit; } numDigits++; result = next; } return(!overflow && numDigits > 0); }
static bool TryParseUInt(ref UString s, ref BigInteger result, int radix, ParseNumberFlag flags, out int numDigits) { // TODO: OPTIMIZE THIS ALGORITHM: it is currently O(n^2) in the number of digits numDigits = 0; if ((flags & ParseNumberFlag.SkipSpacesInFront) != 0) { s = SkipSpaces(s); } for (;; s = s.Slice(1)) { char c = s[0, '\0']; uint digit = (uint)Base36DigitValue(c); if (digit >= radix) { if ((c == ' ' || c == '\t') && (flags & ParseNumberFlag.SkipSpacesInsideNumber) != 0) { continue; } else if (c == '_' && (flags & ParseNumberFlag.SkipUnderscores) != 0) { continue; } else if (c == '\'' && (flags & ParseNumberFlag.SkipSingleQuotes) != 0) { continue; } else { break; } } result = result * (uint)radix + digit; numDigits++; } return(numDigits > 0); }
/// <summary>Parses a string to a single-precision float, returning NaN on /// failure or an infinity value on overflow.</summary> /// <param name="radix">Base of the number to parse; must be 2 (binary), /// 4, 8 (octal), 10 (decimal), 16 (hexadecimal) or 32.</param> /// <param name="flags">Alters parsing behavior, see <see cref="ParseNumberFlag"/>.</param> public static float TryParseFloat(ref UString source, int radix, ParseNumberFlag flags = 0) { ulong mantissa; int exponentBase2, exponentBase10, numDigits; bool negative; if (!TryParseFloatParts(ref source, radix, out negative, out mantissa, out exponentBase2, out exponentBase10, out numDigits, flags)) { return(float.NaN); } else { float num = (float)G.ShiftLeft(mantissa, exponentBase2); if (negative) { num = -num; } if (exponentBase10 == 0) { return(num); } return(num * (float)System.Math.Pow(10, exponentBase10)); } }
static bool TryParseUInt(ref UString s, ref BigInteger result, int radix, ParseNumberFlag flags, out int numDigits) { // TODO: OPTIMIZE THIS ALGORITHM: it is currently O(n^2) in the number of digits numDigits = 0; if ((flags & ParseNumberFlag.SkipSpacesInFront) != 0) s = SkipSpaces(s); for (;; s = s.Slice(1)) { char c = s[0, '\0']; uint digit = (uint)Base36DigitValue(c); if (digit >= radix) { if ((c == ' ' || c == '\t') && (flags & ParseNumberFlag.SkipSpacesInsideNumber) != 0) continue; else if (c == '_' && (flags & ParseNumberFlag.SkipUnderscores) != 0) continue; else if (c == '\'' && (flags & ParseNumberFlag.SkipSingleQuotes) != 0) continue; else break; } result = result * (uint)radix + digit; numDigits++; } return numDigits > 0; }
/// <inheritdoc cref="TryParseUInt(ref UString, out ulong, int, ParseNumberFlag)"/> public static bool TryParseUInt(ref UString s, out BigInteger result, int radix = 10, ParseNumberFlag flags = 0) { result = 0; int _; return TryParseUInt(ref s, ref result, radix, flags, out _); }
static bool TryParseUInt(ref UString s, ref ulong result, int radix, ParseNumberFlag flags, out int numDigits) { numDigits = 0; if ((flags & ParseNumberFlag.SkipSpacesInFront) != 0) s = SkipSpaces(s); bool overflow = false; for (;; s = s.Slice(1)) { char c = s[0, '\0']; uint digit = (uint)Base36DigitValue(c); if (digit >= radix) { if ((c == ' ' || c == '\t') && (flags & ParseNumberFlag.SkipSpacesInsideNumber) != 0) continue; else if (c == '_' && (flags & ParseNumberFlag.SkipUnderscores) != 0) continue; else if (c == '\'' && (flags & ParseNumberFlag.SkipSingleQuotes) != 0) continue; else break; } ulong next; try { next = checked(result * (uint)radix + digit); } catch (OverflowException) { overflow = true; if ((flags & ParseNumberFlag.StopBeforeOverflow) != 0) return false; next = result * (uint)radix + digit; } numDigits++; result = next; } return !overflow && numDigits > 0; }
/// <inheritdoc cref="TryParseInt(string, ref int, out int, int, bool)"/> public static bool TryParseInt(ref UString input, out long result, int radix = 10, ParseNumberFlag flags = 0) { if ((flags & ParseNumberFlag.SkipSpacesInFront) != 0) input = SkipSpaces(input); UString s = input; bool negative = false; char c = s[0, '\0']; if (c == '-' || c == '+') { negative = c == '-'; s = s.Slice(1); } ulong resultU = 0; int numDigits; bool ok = TryParseUInt(ref s, ref resultU, radix, flags, out numDigits); result = negative ? -(long)resultU : (long)resultU; if (numDigits != 0) input = s; return ok && ((result < 0) == negative || result == 0); }
/// <inheritdoc cref="TryParseInt(string, ref int, out int, int, bool)"/> public static bool TryParseInt(ref UString s, out int result, int radix = 10, ParseNumberFlag flags = 0) { long resultL; bool ok = TryParseInt(ref s, out resultL, radix, flags); result = (int)resultL; return ok && result == resultL; }
private void TestParse(bool expectSuccess, int radix, UString input, float expected, ParseNumberFlag flags = 0) { float result = ParseHelpers.TryParseFloat(ref input, radix, flags); bool success = !float.IsNaN(result) && input.IsEmpty; AreEqual(expectSuccess, success); IsTrue(expected == result || expected == MathEx.NextLower(result) || expected == MathEx.NextHigher(result) || float.IsNaN(expected) && float.IsNaN(result)); }
/// <summary>Parses the parts of a floating-point string. See the other /// overload for details.</summary> /// <param name="radix">Base of the number to parse; must be 2 (binary), /// 4, 8 (octal), 10 (decimal), 16 (hexadecimal) or 32.</param> /// <param name="negative">true if the string began with '-'.</param> /// <param name="mantissa">Integer magnitude of the number.</param> /// <param name="exponentBase2">Base-2 exponent to apply.</param> /// <param name="exponentBase10">Base-10 exponent to apply.</param> /// <param name="numDigits">Set to the number of digits in the number, not including the exponent part.</param> /// <param name="flags">Alters parsing behavior, see <see cref="ParseNumberFlag"/>.</param> /// <remarks> /// This method is a wrapper around the other overload that combines /// the 'exponentBaseR' parameter with 'exponentBase2' or 'exponentBase10' /// depending on the radix. For example, when radix=10, this method /// adds <c>exponentBaseR</c> to <c>exponentBase10</c>. /// </remarks> public static bool TryParseFloatParts(ref UString source, int radix, out bool negative, out ulong mantissa, out int exponentBase2, out int exponentBase10, out int numDigits, ParseNumberFlag flags = 0) { int radixShift = 0; if (radix != 10) { radixShift = G.Log2Floor(radix); if (radix > 32 || radix != 1 << radixShift) throw new ArgumentOutOfRangeException("radix"); } int exponentBaseR; bool success = TryParseFloatParts(ref source, radix, out negative, out mantissa, out exponentBaseR, out exponentBase2, out exponentBase10, out numDigits, flags); try { checked { if (radix == 10) exponentBase10 += exponentBaseR; else exponentBase2 += exponentBaseR * radixShift; } } catch (OverflowException) { return false; } return success; }
/// <summary>Low-level method that identifies the parts of a float literal /// of arbitrary base (typically base 2, 10, or 16) with no prefix or /// suffix, such as <c>2.Cp0</c> (which means 2.75 in base 16).</summary> /// <param name="radix">Base of the number to parse; must be between 2 /// and 36.</param> /// <param name="mantissa">Integer magnitude of the number.</param> /// <param name="exponentBase2">Base-2 exponent to apply, as specified by /// the 'p' suffix, or 0 if there is no 'p' suffix..</param> /// <param name="exponentBase10">Base-10 exponent to apply, as specified by /// the 'e' suffix, or 0 if there is no 'e' suffix..</param> /// <param name="exponentBaseR">Base-radix exponent to apply. This number /// is based on the front part of the number only (not including the 'p' or /// 'e' suffix). Negative values represent digits after the decimal point, /// while positive numbers represent 64-bit overflow. For example, if the /// input is <c>12.3456</c> with <c>radix=10</c>, the output will be /// <c>mantissa=123456</c> and <c>exponentBaseR=-4</c>. If the input is /// <c>0123_4567_89AB_CDEF_1234.5678</c> with <c>radix=16</c>, the mantissa /// overflows, and the result is <c>mantissa = 0x1234_5678_9ABC_DEF1</c> /// with <c>exponentBaseR=3</c>.</param> /// <param name="numDigits">Set to the number of digits in the number, not /// including the exponent part.</param> /// <param name="flags">Alters parsing behavior, see <see cref="ParseNumberFlag"/>.</param> /// <remarks> /// The syntax required is /// <code> /// ( '+'|'-' )? /// ( Digits ('.' Digits?)? | '.' Digits ) /// ( ('p'|'P') ('-'|'+')? DecimalDigits+ )? /// ( ('e'|'E') ('-'|'+')? DecimalDigits+ )? /// </code> /// where Digits refers to one or more digits in the requested base, /// possibly including underscores or spaces if the flags allow it; similarly, /// DecimalDigits refers to base-10 digits and is also affected by the /// flags. /// <para/> /// Returns false if there was an error interpreting the input. /// <para/> /// To keep the parser relatively simple, it does not roll back in case of /// error the way the int parser does. For example, given the input "23p", /// the 'p' is consumed and causes the method to return false, even though /// the parse could have been successful if it had ignored the 'p'. /// </remarks> public static bool TryParseFloatParts(ref UString source, int radix, out bool negative, out ulong mantissa, out int exponentBaseR, out int exponentBase2, out int exponentBase10, out int numDigits, ParseNumberFlag flags = 0) { flags |= ParseNumberFlag.StopBeforeOverflow; if ((flags & ParseNumberFlag.SkipSpacesInFront) != 0) { source = SkipSpaces(source); } negative = false; char c = source[0, '\0']; if (c == '-' || c == '+') { negative = c == '-'; source = source.Slice(1); } int numDigits2 = 0; mantissa = 0; exponentBase2 = 0; exponentBase10 = 0; exponentBaseR = 0; bool success = TryParseUInt(ref source, ref mantissa, radix, flags, out numDigits); if (!success) // possible overflow, extra digits remain if so { numDigits += (exponentBaseR = SkipExtraDigits(ref source, radix, flags)); } c = source[0, '\0']; if (c == '.' || (c == ',' && (flags & ParseNumberFlag.AllowCommaDecimalPoint) != 0)) { source = source.Slice(1); if (exponentBaseR == 0) { success = TryParseUInt(ref source, ref mantissa, radix, flags, out numDigits2); if ((numDigits += numDigits2) == 0) { return(false); } exponentBaseR = -numDigits2; } else { Debug.Assert(!success); } if (!success) // possible overflow, extra digits remain if so { numDigits += SkipExtraDigits(ref source, radix, flags); } c = source[0, '\0']; } if (numDigits == 0) { return(false); } success = true; if (c == 'p' || c == 'P') { source = source.Slice(1); success = TryParseInt(ref source, out exponentBase2, 10, flags) && success; c = source[0, '\0']; } if (c == 'e' || c == 'E') { source = source.Slice(1); success = TryParseInt(ref source, out exponentBase10, 10, flags) && success; } return(success); }
/// <inheritdoc cref="TryParseUInt(ref UString, out ulong, int, ParseNumberFlag)"/> public static bool TryParseUInt(ref UString s, out BigInteger result, int radix = 10, ParseNumberFlag flags = 0) { result = 0; int _; return(TryParseUInt(ref s, ref result, radix, flags, out _)); }
/// <inheritdoc cref="TryParseInt(string, ref int, out int, int, bool)"/> public static bool TryParseInt(ref UString input, out long result, int radix = 10, ParseNumberFlag flags = 0) { if ((flags & ParseNumberFlag.SkipSpacesInFront) != 0) { input = SkipSpaces(input); } UString s = input; bool negative = false; char c = s[0, '\0']; if (c == '-' || c == '+') { negative = c == '-'; s = s.Slice(1); } ulong resultU = 0; int numDigits; bool ok = TryParseUInt(ref s, ref resultU, radix, flags, out numDigits); result = negative ? -(long)resultU : (long)resultU; if (numDigits != 0) { input = s; } return(ok && ((result < 0) == negative || result == 0)); }
/// <inheritdoc cref="TryParseInt(string, ref int, out int, int, bool)"/> public static bool TryParseInt(ref UString s, out int result, int radix = 10, ParseNumberFlag flags = 0) { long resultL; bool ok = TryParseInt(ref s, out resultL, radix, flags); result = (int)resultL; return(ok && result == resultL); }
/// <summary>Low-level method that identifies the parts of a float literal /// of arbitrary base (typically base 2, 10, or 16) with no prefix or /// suffix, such as <c>2.Cp0</c> (which means 2.75 in base 16).</summary> /// <param name="radix">Base of the number to parse; must be between 2 /// and 36.</param> /// <param name="mantissa">Integer magnitude of the number.</param> /// <param name="exponentBase2">Base-2 exponent to apply, as specified by /// the 'p' suffix, or 0 if there is no 'p' suffix..</param> /// <param name="exponentBase10">Base-10 exponent to apply, as specified by /// the 'e' suffix, or 0 if there is no 'e' suffix..</param> /// <param name="exponentBaseR">Base-radix exponent to apply. This number /// is based on the front part of the number only (not including the 'p' or /// 'e' suffix). Negative values represent digits after the decimal point, /// while positive numbers represent 64-bit overflow. For example, if the /// input is <c>12.3456</c> with <c>radix=10</c>, the output will be /// <c>mantissa=123456</c> and <c>exponentBaseR=-4</c>. If the input is /// <c>0123_4567_89AB_CDEF_1234.5678</c> with <c>radix=16</c>, the mantissa /// overflows, and the result is <c>mantissa = 0x1234_5678_9ABC_DEF1</c> /// with <c>exponentBaseR=3</c>.</param> /// <param name="numDigits">Set to the number of digits in the number, not /// including the exponent part.</param> /// <param name="flags">Alters parsing behavior, see <see cref="ParseNumberFlag"/>.</param> /// <remarks> /// The syntax required is /// <code> /// ( '+'|'-' )? /// ( Digits ('.' Digits?)? | '.' Digits ) /// ( ('p'|'P') ('-'|'+')? DecimalDigits+ )? /// ( ('e'|'E') ('-'|'+')? DecimalDigits+ )? /// </code> /// where Digits refers to one or more digits in the requested base, /// possibly including underscores or spaces if the flags allow it; similarly, /// DecimalDigits refers to base-10 digits and is also affected by the /// flags. /// <para/> /// Returns false if there was an error interpreting the input. /// <para/> /// To keep the parser relatively simple, it does not roll back in case of /// error the way the int parser does. For example, given the input "23p", /// the 'p' is consumed and causes the method to return false, even though /// the parse could have been successful if it had ignored the 'p'. /// </remarks> public static bool TryParseFloatParts(ref UString source, int radix, out bool negative, out ulong mantissa, out int exponentBaseR, out int exponentBase2, out int exponentBase10, out int numDigits, ParseNumberFlag flags = 0) { flags |= ParseNumberFlag.StopBeforeOverflow; if ((flags & ParseNumberFlag.SkipSpacesInFront) != 0) source = SkipSpaces(source); negative = false; char c = source[0, '\0']; if (c == '-' || c == '+') { negative = c == '-'; source = source.Slice(1); } int numDigits2 = 0; mantissa = 0; exponentBase2 = 0; exponentBase10 = 0; exponentBaseR = 0; bool success = TryParseUInt(ref source, ref mantissa, radix, flags, out numDigits); if (!success) // possible overflow, extra digits remain if so numDigits += (exponentBaseR = SkipExtraDigits(ref source, radix, flags)); c = source[0, '\0']; if (c == '.' || (c == ',' && (flags & ParseNumberFlag.AllowCommaDecimalPoint) != 0)) { source = source.Slice(1); if (exponentBaseR == 0) { success = TryParseUInt(ref source, ref mantissa, radix, flags, out numDigits2); if ((numDigits += numDigits2) == 0) return false; exponentBaseR = -numDigits2; } else Debug.Assert(!success); if (!success) // possible overflow, extra digits remain if so numDigits += SkipExtraDigits(ref source, radix, flags); c = source[0, '\0']; } if (numDigits == 0) return false; success = true; if (c == 'p' || c == 'P') { source = source.Slice(1); success = TryParseInt(ref source, out exponentBase2, 10, flags) && success; c = source[0, '\0']; } if (c == 'e' || c == 'E') { source = source.Slice(1); success = TryParseInt(ref source, out exponentBase10, 10, flags) && success; } return success; }
private static int SkipExtraDigits(ref UString s, int radix, ParseNumberFlag flags) { for (int skipped = 0;; skipped++, s = s.Slice(1)) { char c = s[0, '\0']; uint digit = (uint)Base36DigitValue(c); if (digit >= radix) { if ((c == ' ' || c == '\t') && (flags & ParseNumberFlag.SkipSpacesInsideNumber) != 0) continue; else if (c == '_' && (flags & ParseNumberFlag.SkipUnderscores) != 0) continue; else if (c == '\'' && (flags & ParseNumberFlag.SkipSingleQuotes) != 0) continue; else return skipped; } } }
/// <summary>Parses the parts of a floating-point string. See the other /// overload for details.</summary> /// <param name="radix">Base of the number to parse; must be 2 (binary), /// 4, 8 (octal), 10 (decimal), 16 (hexadecimal) or 32.</param> /// <param name="negative">true if the string began with '-'.</param> /// <param name="mantissa">Integer magnitude of the number.</param> /// <param name="exponentBase2">Base-2 exponent to apply.</param> /// <param name="exponentBase10">Base-10 exponent to apply.</param> /// <param name="numDigits">Set to the number of digits in the number, not including the exponent part.</param> /// <param name="flags">Alters parsing behavior, see <see cref="ParseNumberFlag"/>.</param> /// <remarks> /// This method is a wrapper around the other overload that combines /// the 'exponentBaseR' parameter with 'exponentBase2' or 'exponentBase10' /// depending on the radix. For example, when radix=10, this method /// adds <c>exponentBaseR</c> to <c>exponentBase10</c>. /// </remarks> public static bool TryParseFloatParts(ref UString source, int radix, out bool negative, out ulong mantissa, out int exponentBase2, out int exponentBase10, out int numDigits, ParseNumberFlag flags = 0) { int radixShift = 0; if (radix != 10) { radixShift = G.Log2Floor(radix); if (radix > 32 || radix != 1 << radixShift) { throw new ArgumentOutOfRangeException("radix"); } } int exponentBaseR; bool success = TryParseFloatParts(ref source, radix, out negative, out mantissa, out exponentBaseR, out exponentBase2, out exponentBase10, out numDigits, flags); try { checked { if (radix == 10) { exponentBase10 += exponentBaseR; } else { exponentBase2 += exponentBaseR * radixShift; } } } catch (OverflowException) { return(false); } return(success); }
/// <summary>Parses a string to a single-precision float, returning NaN on /// failure or an infinity value on overflow.</summary> /// <param name="radix">Base of the number to parse; must be 2 (binary), /// 4, 8 (octal), 10 (decimal), 16 (hexadecimal) or 32.</param> /// <param name="flags">Alters parsing behavior, see <see cref="ParseNumberFlag"/>.</param> public static float TryParseFloat(ref UString source, int radix, ParseNumberFlag flags = 0) { ulong mantissa; int exponentBase2, exponentBase10, numDigits; bool negative; if (!TryParseFloatParts(ref source, radix, out negative, out mantissa, out exponentBase2, out exponentBase10, out numDigits, flags)) return float.NaN; else { float num = (float)G.ShiftLeft(mantissa, exponentBase2); if (negative) num = -num; if (exponentBase10 == 0) return num; return num * (float)System.Math.Pow(10, exponentBase10); } }