internal static void FormatBigInteger(ref ValueStringBuilder sb, int precision, int scale, bool sign, ReadOnlySpan <char> format, NumberFormatInfo numberFormatInfo, char[] digits, int startIndex) { unsafe { fixed(char *overrideDigits = digits) { var numberBuffer = new Number.NumberBuffer(); numberBuffer.overrideDigits = overrideDigits + startIndex; numberBuffer.precision = precision; numberBuffer.scale = scale; numberBuffer.sign = sign; char fmt = Number.ParseFormatSpecifier(format, out int maxDigits); if (fmt != 0) { Number.NumberToString(ref sb, ref numberBuffer, fmt, maxDigits, numberFormatInfo, isDecimal: false); } else { Number.NumberToStringFormat(ref sb, ref numberBuffer, format, numberFormatInfo); } } } }
// System.Number internal unsafe static bool TryParseInt32(string s, NumberStyles style, NumberFormatInfo info, out int result) { byte *stackBuffer = stackalloc byte[1 * 114 / 1]; Number.NumberBuffer numberBuffer = new Number.NumberBuffer(stackBuffer); result = 0; if (!Number.TryStringToNumber(s, style, ref numberBuffer, info, false)) { return(false); } if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None) { if (!Number.HexNumberToInt32(ref numberBuffer, ref result)) { return(false); } } else { if (!Number.NumberToInt32(ref numberBuffer, ref result)) { return(false); } } return(true);
internal static unsafe bool TryParseBigInteger(string value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { ArgumentException exception; result = BigInteger.Zero; if (!TryValidateParseStyleInteger(style, out exception)) { throw exception; } BigNumberBuffer number = BigNumberBuffer.Create(); byte * stackBuffer = stackalloc byte[0x72]; Number.NumberBuffer buffer2 = new Number.NumberBuffer(stackBuffer); result = 0; if (!Number.TryStringToNumber(value, style, ref buffer2, number.digits, info, false)) { return(false); } number.precision = buffer2.precision; number.scale = buffer2.scale; number.sign = buffer2.sign; if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None) { if (!HexNumberToBigInteger(ref number, ref result)) { return(false); } } else if (!NumberToBigInteger(ref number, ref result)) { return(false); } return(true); }
/// <summary> /// Parses a Decimal at the start of a Utf8 string. /// </summary> /// <param name="source">The Utf8 string to parse</param> /// <param name="value">Receives the parsed value</param> /// <param name="bytesConsumed">On a successful parse, receives the length in bytes of the substring that was parsed </param> /// <param name="standardFormat">Expected format of the Utf8 string</param> /// <returns> /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. /// </returns> /// <remarks> /// Formats supported: /// G/g (default) /// F/f 12.45 Fixed point /// E/e 1.245000e1 Exponential /// </remarks> /// <exceptions> /// <cref>System.FormatException</cref> if the format is not valid for this data type. /// </exceptions> public static unsafe bool TryParse(ReadOnlySpan <byte> source, out decimal value, out int bytesConsumed, char standardFormat = default) { ParseNumberOptions options; switch (standardFormat) { case default(char): case 'G': case 'g': case 'E': case 'e': options = ParseNumberOptions.AllowExponent; break; case 'F': case 'f': options = default; break; default: return(ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed)); } byte *pDigits = stackalloc byte[Number.DecimalNumberBufferLength]; Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength); if (!TryParseNumber(source, ref number, out bytesConsumed, options, out bool textUsedExponentNotation)) { value = default; return(false); } if ((!textUsedExponentNotation) && (standardFormat == 'E' || standardFormat == 'e')) { value = default; bytesConsumed = 0; return(false); } // More compat with .NET behavior - whether or not a 0 keeps the negative sign depends on whether it an "integer" 0 or a "fractional" 0 if (number.Digits[0] == 0 && number.Scale == 0) { number.IsNegative = false; } value = default; if (!Number.TryNumberToDecimal(ref number, ref value)) { value = default; bytesConsumed = 0; return(false); } return(true); }
public static unsafe bool TryParse(ReadOnlySpan <byte> source, out decimal value, out int bytesConsumed, char standardFormat = default) { ParseNumberOptions options; switch (standardFormat) { case default(char): case 'G': case 'g': case 'E': case 'e': options = ParseNumberOptions.AllowExponent; break; case 'F': case 'f': options = default; break; default: return(ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed)); } byte *pDigits = stackalloc byte[Number.DecimalNumberBufferLength]; Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength); if (!TryParseNumber(source, ref number, out bytesConsumed, options, out bool textUsedExponentNotation)) { value = default; return(false); } if ((!textUsedExponentNotation) && (standardFormat == 'E' || standardFormat == 'e')) { value = default; bytesConsumed = 0; return(false); } value = default; if (!Number.TryNumberToDecimal(ref number, ref value)) { value = default; bytesConsumed = 0; return(false); } return(true); }
public static unsafe bool TryParse(ReadOnlySpan <byte> source, out double value, out int bytesConsumed, char standardFormat = default) { byte *pDigits = stackalloc byte[Number.DoubleNumberBufferLength]; Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.FloatingPoint, pDigits, Number.DoubleNumberBufferLength); if (TryParseNormalAsFloatingPoint(source, ref number, out bytesConsumed, standardFormat)) { value = Number.NumberToDouble(ref number); return(true); } return(TryParseAsSpecialFloatingPoint(source, double.PositiveInfinity, double.NegativeInfinity, double.NaN, out value, out bytesConsumed)); }
internal static string FormatBigInteger(int precision, int scale, bool sign, string format, NumberFormatInfo numberFormatInfo, char[] digits, int startIndex) { unsafe { int maxDigits; char fmt = Number.ParseFormatSpecifier(format, out maxDigits); fixed(char *overrideDigits = digits) { Number.NumberBuffer numberBuffer = new Number.NumberBuffer(); numberBuffer.overrideDigits = overrideDigits + startIndex; numberBuffer.precision = precision; numberBuffer.scale = scale; numberBuffer.sign = sign; if (fmt != 0) { return(Number.NumberToString(numberBuffer, fmt, maxDigits, numberFormatInfo, isDecimal: false)); } return(Number.NumberToStringFormat(numberBuffer, format, numberFormatInfo)); } } }
internal unsafe static Boolean TryParseBigInteger(String value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { result = BigInteger.Zero; ArgumentException e; if (!TryValidateParseStyleInteger(style, out e)) { throw e; // TryParse still throws ArgumentException on invalid NumberStyles } BigNumberBuffer bignumber = BigNumberBuffer.Create(); Byte * numberBufferBytes = stackalloc Byte[Number.NumberBuffer.NumberBufferBytes]; Number.NumberBuffer number = new Number.NumberBuffer(numberBufferBytes); result = 0; if (!Number.TryStringToNumber(value, style, ref number, bignumber.digits, info, false)) { return(false); } bignumber.precision = number.precision; bignumber.scale = number.scale; bignumber.sign = number.sign; if ((style & NumberStyles.AllowHexSpecifier) != 0) { if (!HexNumberToBigInteger(ref bignumber, ref result)) { return(false); } } else { if (!NumberToBigInteger(ref bignumber, ref result)) { return(false); } } return(true); }
static String FormatBigInteger(BigInteger value, String format, NumberFormatInfo info) { int digits = 0; char fmt = ParseFormatSpecifier(format, out digits); if (fmt == 'x' || fmt == 'X') { return(FormatBigIntegerToHexString(value, fmt, digits, info)); } bool decimalFmt = (fmt == 'g' || fmt == 'G' || fmt == 'd' || fmt == 'D' || fmt == 'r' || fmt == 'R'); #if SILVERLIGHT || FEATURE_NETCORE || MONO if (!decimalFmt) { // Silverlight supports invariant formats only throw new FormatException(SR.GetString(SR.Format_InvalidFormatSpecifier)); } #endif //SILVERLIGHT ||FEATURE_NETCORE || MONO if (value._bits == null) { if (fmt == 'g' || fmt == 'G' || fmt == 'r' || fmt == 'R') { if (digits > 0) { format = String.Format(CultureInfo.InvariantCulture, "D{0}", digits.ToString(CultureInfo.InvariantCulture)); } else { format = "D"; } } return(value._sign.ToString(format, info)); } // First convert to base 10^9. const uint kuBase = 1000000000; // 10^9 const int kcchBase = 9; int cuSrc = BigInteger.Length(value._bits); int cuMax; try { cuMax = checked (cuSrc * 10 / 9 + 2); } catch (OverflowException e) { throw new FormatException(SR.GetString(SR.Format_TooLarge), e); } uint[] rguDst = new uint[cuMax]; int cuDst = 0; for (int iuSrc = cuSrc; --iuSrc >= 0;) { uint uCarry = value._bits[iuSrc]; for (int iuDst = 0; iuDst < cuDst; iuDst++) { Contract.Assert(rguDst[iuDst] < kuBase); ulong uuRes = NumericsHelpers.MakeUlong(rguDst[iuDst], uCarry); rguDst[iuDst] = (uint)(uuRes % kuBase); uCarry = (uint)(uuRes / kuBase); } if (uCarry != 0) { rguDst[cuDst++] = uCarry % kuBase; uCarry /= kuBase; if (uCarry != 0) { rguDst[cuDst++] = uCarry; } } } int cchMax; try { // Each uint contributes at most 9 digits to the decimal representation. cchMax = checked (cuDst * kcchBase); } catch (OverflowException e) { throw new FormatException(SR.GetString(SR.Format_TooLarge), e); } if (decimalFmt) { if (digits > 0 && digits > cchMax) { cchMax = digits; } if (value._sign < 0) { try { // Leave an extra slot for a minus sign. cchMax = checked (cchMax + info.NegativeSign.Length); } catch (OverflowException e) { throw new FormatException(SR.GetString(SR.Format_TooLarge), e); } } } int rgchBufSize; try { // We'll pass the rgch buffer to native code, which is going to treat it like a string of digits, so it needs // to be null terminated. Let's ensure that we can allocate a buffer of that size. rgchBufSize = checked (cchMax + 1); } catch (OverflowException e) { throw new FormatException(SR.GetString(SR.Format_TooLarge), e); } char[] rgch = new char[rgchBufSize]; int ichDst = cchMax; for (int iuDst = 0; iuDst < cuDst - 1; iuDst++) { uint uDig = rguDst[iuDst]; Contract.Assert(uDig < kuBase); for (int cch = kcchBase; --cch >= 0;) { rgch[--ichDst] = (char)('0' + uDig % 10); uDig /= 10; } } for (uint uDig = rguDst[cuDst - 1]; uDig != 0;) { rgch[--ichDst] = (char)('0' + uDig % 10); uDig /= 10; } #if !SILVERLIGHT || FEATURE_NETCORE if (!decimalFmt) { // // Go to the VM for GlobLoc aware formatting // Byte *numberBufferBytes = stackalloc Byte[Number.NumberBuffer.NumberBufferBytes]; Number.NumberBuffer number = new Number.NumberBuffer(numberBufferBytes); // sign = true for negative and false for 0 and positive values number.sign = (value._sign < 0); // the cut-off point to switch (G)eneral from (F)ixed-point to (E)xponential form number.precision = 29; number.digits[0] = '\0'; number.scale = cchMax - ichDst; int maxDigits = Math.Min(ichDst + 50, cchMax); for (int i = ichDst; i < maxDigits; i++) { number.digits[i - ichDst] = rgch[i]; } fixed(char *pinnedExtraDigits = rgch) { return(Number.FormatNumberBuffer(number.PackForNative(), format, info, pinnedExtraDigits + ichDst)); } } #endif //!SILVERLIGHT ||FEATURE_NETCORE // Format Round-trip decimal // This format is supported for integral types only. The number is converted to a string of // decimal digits (0-9), prefixed by a minus sign if the number is negative. The precision // specifier indicates the minimum number of digits desired in the resulting string. If required, // the number is padded with zeros to its left to produce the number of digits given by the // precision specifier. int numDigitsPrinted = cchMax - ichDst; while (digits > 0 && digits > numDigitsPrinted) { // pad leading zeros rgch[--ichDst] = '0'; digits--; } if (value._sign < 0) { String negativeSign = info.NegativeSign; for (int i = info.NegativeSign.Length - 1; i > -1; i--) { rgch[--ichDst] = info.NegativeSign[i]; } } return(new String(rgch, ichDst, cchMax - ichDst)); }
internal static unsafe string FormatBigInteger(BigInteger value, string format, NumberFormatInfo info) { int num3; int num9; int digits = 0; char ch = ParseFormatSpecifier(format, out digits); switch (ch) { case 'x': case 'X': return(FormatBigIntegerToHexString(value, ch, digits, info)); } bool flag = ((((ch == 'g') || (ch == 'G')) || ((ch == 'd') || (ch == 'D'))) || (ch == 'r')) || (ch == 'R'); if (value._bits == null) { switch (ch) { case 'g': case 'G': case 'r': case 'R': if (digits > 0) { format = string.Format(CultureInfo.InvariantCulture, "D{0}", new object[] { digits.ToString(CultureInfo.InvariantCulture) }); } else { format = "D"; } break; } return(value._sign.ToString(format, info)); } int num2 = BigInteger.Length(value._bits); try { num3 = ((num2 * 10) / 9) + 2; } catch (OverflowException exception) { throw new FormatException(SR.GetString("Format_TooLarge"), exception); } uint[] numArray = new uint[num3]; int num4 = 0; int index = num2; while (--index >= 0) { uint uLo = value._bits[index]; for (int k = 0; k < num4; k++) { ulong num8 = NumericsHelpers.MakeUlong(numArray[k], uLo); numArray[k] = (uint)(num8 % ((ulong)0x3b9aca00L)); uLo = (uint)(num8 / ((ulong)0x3b9aca00L)); } if (uLo != 0) { numArray[num4++] = uLo % 0x3b9aca00; uLo /= 0x3b9aca00; if (uLo != 0) { numArray[num4++] = uLo; } } } try { num9 = num4 * 9; } catch (OverflowException exception2) { throw new FormatException(SR.GetString("Format_TooLarge"), exception2); } if (flag) { if ((digits > 0) && (digits > num9)) { num9 = digits; } if (value._sign < 0) { try { num9 += info.NegativeSign.Length; } catch (OverflowException exception3) { throw new FormatException(SR.GetString("Format_TooLarge"), exception3); } } } char[] chArray = new char[num9]; int startIndex = num9; for (int i = 0; i < (num4 - 1); i++) { uint num12 = numArray[i]; int num13 = 9; while (--num13 >= 0) { chArray[--startIndex] = (char)(0x30 + (num12 % 10)); num12 /= 10; } } for (uint j = numArray[num4 - 1]; j != 0; j /= 10) { chArray[--startIndex] = (char)(0x30 + (j % 10)); } if (!flag) { byte *stackBuffer = stackalloc byte[0x72]; Number.NumberBuffer buffer = new Number.NumberBuffer(stackBuffer) { sign = value._sign < 0, precision = 0x1d }; buffer.digits[0] = '\0'; buffer.scale = num9 - startIndex; int num15 = Math.Min(startIndex + 50, num9); for (int m = startIndex; m < num15; m++) { buffer.digits[m - startIndex] = chArray[m]; } return(Number.FormatNumberBuffer(buffer.PackForNative(), format, info)); } int num17 = num9 - startIndex; while ((digits > 0) && (digits > num17)) { chArray[--startIndex] = '0'; digits--; } if (value._sign < 0) { string negativeSign = info.NegativeSign; for (int n = info.NegativeSign.Length - 1; n > -1; n--) { chArray[--startIndex] = info.NegativeSign[n]; } } return(new string(chArray, startIndex, num9 - startIndex)); }
private static bool TryFormatDecimalG(ref Number.NumberBuffer number, Span <byte> destination, out int bytesWritten) { int scale = number.Scale; ReadOnlySpan <byte> digits = number.Digits; int numDigits = number.DigitsCount; bool isFraction = scale < numDigits; int numBytesNeeded; if (isFraction) { numBytesNeeded = numDigits + 1; // A fraction. Must include one for the decimal point. if (scale <= 0) { numBytesNeeded += 1 + (-scale); // A fraction of the form 0.ddd. Need to emit the non-stored 0 before the decimal point plus (-scale) leading 0's after the decimal point. } } else { numBytesNeeded = ((scale <= 0) ? 1 : scale); // An integral. Just emit the digits before the decimal point (minimum 1) and no decimal point. } if (number.IsNegative) { numBytesNeeded++; // And the minus sign. } if (destination.Length < numBytesNeeded) { bytesWritten = 0; return(false); } int srcIndex = 0; int dstIndex = 0; if (number.IsNegative) { destination[dstIndex++] = Utf8Constants.Minus; } // // Emit digits before the decimal point. // if (scale <= 0) { destination[dstIndex++] = (byte)'0'; // The integer portion is 0 and not stored. The formatter, however, needs to emit it. } else { while (srcIndex < scale) { byte digit = digits[srcIndex]; if (digit == 0) { int numTrailingZeroes = scale - srcIndex; for (int i = 0; i < numTrailingZeroes; i++) { destination[dstIndex++] = (byte)'0'; } break; } destination[dstIndex++] = digit; srcIndex++; } } if (isFraction) { destination[dstIndex++] = Utf8Constants.Period; // // Emit digits after the decimal point. // if (scale < 0) { int numLeadingZeroesToEmit = -scale; for (int i = 0; i < numLeadingZeroesToEmit; i++) { destination[dstIndex++] = (byte)'0'; } } byte digit; while ((digit = digits[srcIndex++]) != 0) { destination[dstIndex++] = digit; } } Debug.Assert(dstIndex == numBytesNeeded); bytesWritten = numBytesNeeded; return(true); }
private static bool TryParseNormalAsFloatingPoint(ReadOnlySpan <byte> source, ref Number.NumberBuffer number, out int bytesConsumed, char standardFormat) { ParseNumberOptions options; switch (standardFormat) { case default(char): case 'G': case 'g': case 'E': case 'e': options = ParseNumberOptions.AllowExponent; break; case 'F': case 'f': options = default; break; default: return(ParserHelpers.TryParseThrowFormatException(out bytesConsumed)); } if (!TryParseNumber(source, ref number, out bytesConsumed, options, out bool textUsedExponentNotation)) { return(false); } if ((!textUsedExponentNotation) && (standardFormat == 'E' || standardFormat == 'e')) { bytesConsumed = 0; return(false); } return(true); }
// System.Number private unsafe static bool TryStringToNumber(string str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal) { if (str == null) { return(false); } fixed(char *ptr = str) { char *ptr2 = ptr; if (!Number.ParseNumber(ref ptr2, options, ref number, numfmt, parseDecimal) || ((ptr2 - ptr / 2) / 2 < str.Length && !Number.TrailingZeros(str, (ptr2 - ptr / 2) / 2))) { return(false); } } return(true); }
private static bool TryFormatDecimalF(ref Number.NumberBuffer number, Span <byte> destination, out int bytesWritten, byte precision) { int scale = number.Scale; ReadOnlySpan <byte> digits = number.Digits; int numBytesNeeded = ((number.IsNegative) ? 1 : 0) // minus sign + ((scale <= 0) ? 1 : scale) // digits before the decimal point (minimum 1) + ((precision == 0) ? 0 : (precision + 1)); // if specified precision != 0, the decimal point and the digits after the decimal point (padded with zeroes if needed) if (destination.Length < numBytesNeeded) { bytesWritten = 0; return(false); } int srcIndex = 0; int dstIndex = 0; if (number.IsNegative) { destination[dstIndex++] = Utf8Constants.Minus; } // // Emit digits before the decimal point. // if (scale <= 0) { destination[dstIndex++] = (byte)'0'; // The integer portion is 0 and not stored. The formatter, however, needs to emit it. } else { while (srcIndex < scale) { byte digit = digits[srcIndex]; if (digit == 0) { int numTrailingZeroes = scale - srcIndex; for (int i = 0; i < numTrailingZeroes; i++) { destination[dstIndex++] = (byte)'0'; } break; } destination[dstIndex++] = digit; srcIndex++; } } if (precision > 0) { destination[dstIndex++] = Utf8Constants.Period; // // Emit digits after the decimal point. // int numDigitsEmitted = 0; if (scale < 0) { int numLeadingZeroesToEmit = Math.Min((int)precision, -scale); for (int i = 0; i < numLeadingZeroesToEmit; i++) { destination[dstIndex++] = (byte)'0'; } numDigitsEmitted += numLeadingZeroesToEmit; } while (numDigitsEmitted < precision) { byte digit = digits[srcIndex]; if (digit == 0) { while (numDigitsEmitted++ < precision) { destination[dstIndex++] = (byte)'0'; } break; } destination[dstIndex++] = digit; srcIndex++; numDigitsEmitted++; } } Debug.Assert(dstIndex == numBytesNeeded); bytesWritten = numBytesNeeded; return(true); }
private static bool TryParseNumber(ReadOnlySpan <byte> source, ref Number.NumberBuffer number, out int bytesConsumed, ParseNumberOptions options, out bool textUsedExponentNotation) { Debug.Assert(number.DigitsCount == 0); Debug.Assert(number.Scale == 0); Debug.Assert(number.IsNegative == false); Debug.Assert(number.HasNonZeroTail == false); number.CheckConsistency(); textUsedExponentNotation = false; if (source.Length == 0) { bytesConsumed = 0; return(false); } Span <byte> digits = number.Digits; int srcIndex = 0; int dstIndex = 0; // Consume the leading sign if any. byte c = source[srcIndex]; switch (c) { case Utf8Constants.Minus: number.IsNegative = true; goto case Utf8Constants.Plus; case Utf8Constants.Plus: srcIndex++; if (srcIndex == source.Length) { bytesConsumed = 0; return(false); } c = source[srcIndex]; break; default: break; } int startIndexDigitsBeforeDecimal = srcIndex; int digitCount = 0; int maxDigitCount = digits.Length - 1; // Throw away any leading zeroes while (srcIndex != source.Length) { c = source[srcIndex]; if (c != '0') { break; } srcIndex++; } if (srcIndex == source.Length) { bytesConsumed = srcIndex; number.CheckConsistency(); return(true); } int startIndexNonLeadingDigitsBeforeDecimal = srcIndex; int hasNonZeroTail = 0; while (srcIndex != source.Length) { c = source[srcIndex]; int value = (byte)(c - (byte)('0')); if (value > 9) { break; } srcIndex++; digitCount++; if (digitCount >= maxDigitCount) { // For decimal and binary floating-point numbers, we only // need to store digits up to maxDigCount. However, we still // need to keep track of whether any additional digits past // maxDigCount were non-zero, as that can impact rounding // for an input that falls evenly between two representable // results. hasNonZeroTail |= value; } } number.HasNonZeroTail = (hasNonZeroTail != 0); int numDigitsBeforeDecimal = srcIndex - startIndexDigitsBeforeDecimal; int numNonLeadingDigitsBeforeDecimal = srcIndex - startIndexNonLeadingDigitsBeforeDecimal; Debug.Assert(dstIndex == 0); int numNonLeadingDigitsBeforeDecimalToCopy = Math.Min(numNonLeadingDigitsBeforeDecimal, maxDigitCount); source.Slice(startIndexNonLeadingDigitsBeforeDecimal, numNonLeadingDigitsBeforeDecimalToCopy).CopyTo(digits); dstIndex = numNonLeadingDigitsBeforeDecimalToCopy; number.Scale = numNonLeadingDigitsBeforeDecimal; if (srcIndex == source.Length) { digits[dstIndex] = 0; number.DigitsCount = dstIndex; bytesConsumed = srcIndex; number.CheckConsistency(); return(true); } int numDigitsAfterDecimal = 0; if (c == Utf8Constants.Period) { // // Parse the digits after the decimal point. // srcIndex++; int startIndexDigitsAfterDecimal = srcIndex; while (srcIndex != source.Length) { c = source[srcIndex]; int value = (byte)(c - (byte)('0')); if (value > 9) { break; } srcIndex++; digitCount++; if (digitCount >= maxDigitCount) { // For decimal and binary floating-point numbers, we only // need to store digits up to maxDigCount. However, we still // need to keep track of whether any additional digits past // maxDigCount were non-zero, as that can impact rounding // for an input that falls evenly between two representable // results. hasNonZeroTail |= value; } } number.HasNonZeroTail = (hasNonZeroTail != 0); numDigitsAfterDecimal = srcIndex - startIndexDigitsAfterDecimal; int startIndexOfDigitsAfterDecimalToCopy = startIndexDigitsAfterDecimal; if (dstIndex == 0) { // Not copied any digits to the Number struct yet. This means we must continue discarding leading zeroes even though they appeared after the decimal point. while (startIndexOfDigitsAfterDecimalToCopy < srcIndex && source[startIndexOfDigitsAfterDecimalToCopy] == '0') { number.Scale--; startIndexOfDigitsAfterDecimalToCopy++; } } int numDigitsAfterDecimalToCopy = Math.Min(srcIndex - startIndexOfDigitsAfterDecimalToCopy, maxDigitCount - dstIndex); source.Slice(startIndexOfDigitsAfterDecimalToCopy, numDigitsAfterDecimalToCopy).CopyTo(digits.Slice(dstIndex)); dstIndex += numDigitsAfterDecimalToCopy; // We "should" really NUL terminate, but there are multiple places we'd have to do this and it is a precondition that the caller pass in a fully zero=initialized Number. if (srcIndex == source.Length) { if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0) { // For compatibility. You can say "5." and ".5" but you can't say "." bytesConsumed = 0; return(false); } digits[dstIndex] = 0; number.DigitsCount = dstIndex; bytesConsumed = srcIndex; number.CheckConsistency(); return(true); } } if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0) { bytesConsumed = 0; return(false); } if ((c & ~0x20u) != 'E') { digits[dstIndex] = 0; number.DigitsCount = dstIndex; bytesConsumed = srcIndex; number.CheckConsistency(); return(true); } // // Parse the exponent after the "E" // textUsedExponentNotation = true; srcIndex++; if ((options & ParseNumberOptions.AllowExponent) == 0) { bytesConsumed = 0; return(false); } if (srcIndex == source.Length) { bytesConsumed = 0; return(false); } bool exponentIsNegative = false; c = source[srcIndex]; switch (c) { case Utf8Constants.Minus: exponentIsNegative = true; goto case Utf8Constants.Plus; case Utf8Constants.Plus: srcIndex++; if (srcIndex == source.Length) { bytesConsumed = 0; return(false); } c = source[srcIndex]; break; default: break; } // If the next character isn't a digit, an exponent wasn't specified if ((byte)(c - (byte)('0')) > 9) { bytesConsumed = 0; return(false); } if (!TryParseUInt32D(source.Slice(srcIndex), out uint absoluteExponent, out int bytesConsumedByExponent)) { // Since we found at least one digit, we know that any failure to parse means we had an // exponent that was larger than uint.MaxValue, and we can just eat characters until the end absoluteExponent = uint.MaxValue; // This also means that we know there was at least 10 characters and we can "eat" those, and // continue eating digits from there srcIndex += 10; while (srcIndex != source.Length) { c = source[srcIndex]; int value = (byte)(c - (byte)('0')); if (value > 9) { break; } srcIndex++; } } srcIndex += bytesConsumedByExponent; if (exponentIsNegative) { if (number.Scale < int.MinValue + (long)absoluteExponent) { // A scale underflow means all non-zero digits are all so far to the right of the decimal point, no // number format we have will be able to see them. Just pin the scale at the absolute minimum // and let the converter produce a 0 with the max precision available for that type. number.Scale = int.MinValue; } else { number.Scale -= (int)absoluteExponent; } } else { if (number.Scale > int.MaxValue - (long)absoluteExponent) { // A scale overflow means all non-zero digits are all so far to the right of the decimal point, no // number format we have will be able to see them. Just pin the scale at the absolute maximum // and let the converter produce a 0 with the max precision available for that type. number.Scale = int.MaxValue; } else { number.Scale += (int)absoluteExponent; } } digits[dstIndex] = 0; number.DigitsCount = dstIndex; bytesConsumed = srcIndex; number.CheckConsistency(); return(true); }
private static bool TryFormatDecimalE(ref Number.NumberBuffer number, Span <byte> destination, out int bytesWritten, byte precision, byte exponentSymbol) { const int NumExponentDigits = 3; int scale = number.Scale; ReadOnlySpan <byte> digits = number.Digits; int numBytesNeeded = ((number.IsNegative) ? 1 : 0) // minus sign + 1 // digits before the decimal point (exactly 1) + ((precision == 0) ? 0 : (precision + 1)) // period and the digits after the decimal point + 2 // 'E' or 'e' followed by '+' or '-' + NumExponentDigits; // exponent digits if (destination.Length < numBytesNeeded) { bytesWritten = 0; return(false); } int dstIndex = 0; int srcIndex = 0; if (number.IsNegative) { destination[dstIndex++] = Utf8Constants.Minus; } // // Emit exactly one digit before the decimal point. // int exponent; byte firstDigit = digits[srcIndex]; if (firstDigit == 0) { destination[dstIndex++] = (byte)'0'; // Special case: number before the decimal point is exactly 0: Number does not store the zero in this case. exponent = 0; } else { destination[dstIndex++] = firstDigit; srcIndex++; exponent = scale - 1; } if (precision > 0) { destination[dstIndex++] = Utf8Constants.Period; // // Emit digits after the decimal point. // int numDigitsEmitted = 0; while (numDigitsEmitted < precision) { byte digit = digits[srcIndex]; if (digit == 0) { while (numDigitsEmitted++ < precision) { destination[dstIndex++] = (byte)'0'; } break; } destination[dstIndex++] = digit; srcIndex++; numDigitsEmitted++; } } // Emit the exponent symbol destination[dstIndex++] = exponentSymbol; if (exponent >= 0) { destination[dstIndex++] = Utf8Constants.Plus; } else { destination[dstIndex++] = Utf8Constants.Minus; exponent = -exponent; } Debug.Assert(exponent < Number.DecimalPrecision, "If you're trying to reuse this routine for double/float, you'll need to review the code carefully for Decimal-specific assumptions."); // Emit exactly three digits for the exponent. destination[dstIndex++] = (byte)'0'; // The exponent for Decimal can never exceed 28 (let alone 99) destination[dstIndex++] = (byte)((exponent / 10) + '0'); destination[dstIndex++] = (byte)((exponent % 10) + '0'); Debug.Assert(dstIndex == numBytesNeeded); bytesWritten = numBytesNeeded; return(true); }
// System.Number private unsafe static bool ParseNumber(ref char *str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal) { number.scale = 0; number.sign = false; string text = null; string text2 = null; string str2 = null; string str3 = null; bool flag = false; string str4; string str5; if ((options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None) { text = numfmt.CurrencySymbol; if (numfmt.ansiCurrencySymbol != null) { text2 = numfmt.ansiCurrencySymbol; } str2 = numfmt.NumberDecimalSeparator; str3 = numfmt.NumberGroupSeparator; str4 = numfmt.CurrencyDecimalSeparator; str5 = numfmt.CurrencyGroupSeparator; flag = true; } else { str4 = numfmt.NumberDecimalSeparator; str5 = numfmt.NumberGroupSeparator; } int num = 0; char *ptr = str; char c = *ptr; while (true) { if (!Number.IsWhite(c) || (options & NumberStyles.AllowLeadingWhite) == NumberStyles.None || ((num & 1) != 0 && ((num & 1) == 0 || ((num & 32) == 0 && numfmt.numberNegativePattern != 2)))) { bool flag2; char *ptr2; if ((flag2 = ((options & NumberStyles.AllowLeadingSign) != NumberStyles.None && (num & 1) == 0)) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null) { num |= 1; ptr = ptr2 - (IntPtr)2 / 2; } else { if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null) { num |= 1; number.sign = true; ptr = ptr2 - (IntPtr)2 / 2; } else { if (c == '(' && (options & NumberStyles.AllowParentheses) != NumberStyles.None && (num & 1) == 0) { num |= 3; number.sign = true; } else { if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null)) { break; } num |= 32; text = null; text2 = null; ptr = ptr2 - (IntPtr)2 / 2; } } } } c = *(ptr += (IntPtr)2 / 2); } int num2 = 0; int num3 = 0; while (true) { if ((c >= '0' && c <= '9') || ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))) { num |= 4; if (c != '0' || (num & 8) != 0) { if (num2 < 50) { number.digits[(IntPtr)(num2++)] = c; if (c != '0' || parseDecimal) { num3 = num2; } } if ((num & 16) == 0) { number.scale++; } num |= 8; } else { if ((num & 16) != 0) { number.scale--; } } } else { char *ptr2; if ((options & NumberStyles.AllowDecimalPoint) != NumberStyles.None && (num & 16) == 0 && ((ptr2 = Number.MatchChars(ptr, str4)) != null || (flag && (num & 32) == 0 && (ptr2 = Number.MatchChars(ptr, str2)) != null))) { num |= 16; ptr = ptr2 - (IntPtr)2 / 2; } else { if ((options & NumberStyles.AllowThousands) == NumberStyles.None || (num & 4) == 0 || (num & 16) != 0 || ((ptr2 = Number.MatchChars(ptr, str5)) == null && (!flag || (num & 32) != 0 || (ptr2 = Number.MatchChars(ptr, str3)) == null))) { break; } ptr = ptr2 - (IntPtr)2 / 2; } } c = *(ptr += (IntPtr)2 / 2); } bool flag3 = false; number.precision = num3; number.digits[(IntPtr)num3] = '\0'; if ((num & 4) != 0) { if ((c == 'E' || c == 'e') && (options & NumberStyles.AllowExponent) != NumberStyles.None) { char *ptr3 = ptr; c = *(ptr += (IntPtr)2 / 2); char *ptr2; if ((ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null) { c = *(ptr = ptr2); } else { if ((ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null) { c = *(ptr = ptr2); flag3 = true; } } if (c >= '0' && c <= '9') { int num4 = 0; do { num4 = num4 * 10 + (int)(c - '0'); c = *(ptr += (IntPtr)2 / 2); if (num4 > 1000) { num4 = 9999; while (c >= '0' && c <= '9') { c = *(ptr += (IntPtr)2 / 2); } } }while (c >= '0' && c <= '9'); if (flag3) { num4 = -num4; } number.scale += num4; } else { ptr = ptr3; c = *ptr; } } while (true) { if (!Number.IsWhite(c) || (options & NumberStyles.AllowTrailingWhite) == NumberStyles.None) { bool flag2; char *ptr2; if ((flag2 = ((options & NumberStyles.AllowTrailingSign) != NumberStyles.None && (num & 1) == 0)) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null) { num |= 1; ptr = ptr2 - (IntPtr)2 / 2; } else { if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null) { num |= 1; number.sign = true; ptr = ptr2 - (IntPtr)2 / 2; } else { if (c == ')' && (num & 2) != 0) { num &= -3; } else { if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null)) { break; } text = null; text2 = null; ptr = ptr2 - (IntPtr)2 / 2; } } } } c = *(ptr += (IntPtr)2 / 2); } if ((num & 2) == 0) { if ((num & 8) == 0) { if (!parseDecimal) { number.scale = 0; } if ((num & 16) == 0) { number.sign = false; } } str = ptr; return(true); } } str = ptr; return(false); }
/// <summary> /// Formats a Decimal as a UTF8 string. /// </summary> /// <param name="value">Value to format</param> /// <param name="destination">Buffer to write the UTF8-formatted value to</param> /// <param name="bytesWritten">Receives the length of the formatted text in bytes</param> /// <param name="format">The standard format to use</param> /// <returns> /// true for success. "bytesWritten" contains the length of the formatted text in bytes. /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. /// </returns> /// <remarks> /// Formats supported: /// G/g (default) /// F/f 12.45 Fixed point /// E/e 1.245000e1 Exponential /// </remarks> /// <exceptions> /// <cref>System.FormatException</cref> if the format is not valid for this data type. /// </exceptions> public static unsafe bool TryFormat(decimal value, Span <byte> destination, out int bytesWritten, StandardFormat format = default) { if (format.IsDefault) { format = 'G'; } switch (format.Symbol) { case 'g': case 'G': { if (format.Precision != StandardFormat.NoPrecision) { throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); } byte *pDigits = stackalloc byte[Number.DecimalNumberBufferLength]; Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength); Number.DecimalToNumber(ref value, ref number); if (number.Digits[0] == 0) { number.IsNegative = false; // For Decimals, -0 must print as normal 0. } bool success = TryFormatDecimalG(ref number, destination, out bytesWritten); #if DEBUG // This DEBUG segment exists to close a code coverage hole inside TryFormatDecimalG(). Because we don't call RoundNumber() on this path, we have no way to feed // TryFormatDecimalG() a number where trailing zeros before the decimal point have been cropped. So if the chance comes up, we'll crop the zeroes // ourselves and make a second call to ensure we get the same outcome. if (success) { Span <byte> digits = number.Digits; int numDigits = number.DigitsCount; if (numDigits != 0 && number.Scale == numDigits && digits[numDigits - 1] == '0') { while (numDigits != 0 && digits[numDigits - 1] == '0') { digits[numDigits - 1] = 0; numDigits--; } number.DigitsCount = numDigits; number.CheckConsistency(); byte[] buffer2 = new byte[destination.Length]; bool success2 = TryFormatDecimalG(ref number, buffer2, out int bytesWritten2); Debug.Assert(success2); Debug.Assert(bytesWritten2 == bytesWritten); for (int i = 0; i < bytesWritten; i++) { Debug.Assert(destination[i] == buffer2[i]); } } } #endif // DEBUG return(success); } case 'f': case 'F': { byte *pDigits = stackalloc byte[Number.DecimalNumberBufferLength]; Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength); Number.DecimalToNumber(ref value, ref number); byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)2 : format.Precision; Number.RoundNumber(ref number, number.Scale + precision, isCorrectlyRounded: false); Debug.Assert((number.Digits[0] != 0) || !number.IsNegative); // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant. return(TryFormatDecimalF(ref number, destination, out bytesWritten, precision)); } case 'e': case 'E': { byte *pDigits = stackalloc byte[Number.DecimalNumberBufferLength]; Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength); Number.DecimalToNumber(ref value, ref number); byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)6 : format.Precision; Number.RoundNumber(ref number, precision + 1, isCorrectlyRounded: false); Debug.Assert((number.Digits[0] != 0) || !number.IsNegative); // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant. return(TryFormatDecimalE(ref number, destination, out bytesWritten, precision, exponentSymbol: (byte)format.Symbol)); } default: return(FormattingHelpers.TryFormatThrowFormatException(out bytesWritten)); } }