private static Boolean HexNumberToInt64(ref NumberBuffer number, ref Int64 value) { UInt64 passedValue = 0; Boolean returnValue = HexNumberToUInt64(ref number, ref passedValue); value = (Int64)passedValue; return returnValue; }
private static bool HexNumberToInt32(ref NumberBuffer number, ref int value) { uint num = 0; bool flag = HexNumberToUInt32(ref number, ref num); value = (int) num; return flag; }
private static bool HexNumberToInt64(ref NumberBuffer number, ref long value) { ulong num = 0L; bool flag = HexNumberToUInt64(ref number, ref num); value = (long) num; return flag; }
private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number) { number.precision = precision; if (DoubleHelper.Exponent(value) == 0x7ff) { number.scale = DoubleHelper.Mantissa(value) != 0 ? SCALE_NAN : SCALE_INF; number.sign = DoubleHelper.Sign(value); number.digits[0] = '\0'; } else { byte* src = stackalloc byte[_CVTBUFSIZE]; int sign; fixed (NumberBuffer* pNumber = &number) { RuntimeImports._ecvt_s(src, _CVTBUFSIZE, value, precision, &pNumber->scale, &sign); } number.sign = sign != 0; char* dst = number.digits; if ((char)*src != '0') { while (*src != 0) *dst++ = (char)*src++; } *dst = '\0'; } }
private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number) { number.precision = precision; if (DoubleHelper.Exponent(value) == 0x7ff) { number.scale = DoubleHelper.Mantissa(value) != 0 ? SCALE_NAN : SCALE_INF; number.sign = DoubleHelper.Sign(value); number.digits[0] = '\0'; } else { byte *src = stackalloc byte[_CVTBUFSIZE]; int sign; fixed(NumberBuffer *pNumber = &number) { RuntimeImports._ecvt_s(src, _CVTBUFSIZE, value, precision, &pNumber->scale, &sign); } number.sign = sign != 0; char *dst = number.digits; if ((char)*src != '0') { while (*src != 0) { *dst++ = (char)*src++; } } *dst = '\0'; } }
//public static bool TryRunHalf(Half value, int requestedDigits, ref NumberBuffer number) //{ // Half v = Half.IsNegative(value) ? Half.Negate(value) : value; // Debug.Assert((double)v > 0); // Debug.Assert(Half.IsFinite(v)); // int length; // int decimalExponent; // bool result; // if (requestedDigits == -1) // { // DiyFp w = DiyFp.CreateAndGetBoundaries(v, out DiyFp boundaryMinus, out DiyFp boundaryPlus).Normalize(); // result = TryRunShortest(in boundaryMinus, in w, in boundaryPlus, number.Digits, out length, out decimalExponent); // } // else // { // DiyFp w = new DiyFp(v).Normalize(); // result = TryRunCounted(in w, requestedDigits, number.Digits, out length, out decimalExponent); // } // if (result) // { // Debug.Assert((requestedDigits == -1) || (length == requestedDigits)); // number.Scale = length + decimalExponent; // number.Digits[length] = (byte)('\0'); // number.DigitsCount = length; // } // return result; //} public static bool TryRunSingle(float value, int requestedDigits, ref NumberBuffer number) { var v = value.IsNegative() ? -value : value; Debug.Assert(v > 0); Debug.Assert(v.IsFinite()); int length; int decimalExponent; bool result; if (requestedDigits == -1) { var w = DiyFp.CreateAndGetBoundaries(v, out var boundaryMinus, out var boundaryPlus).Normalize(); result = TryRunShortest(in boundaryMinus, in w, in boundaryPlus, number.DigitsMut, out length, out decimalExponent); } else { var w = new DiyFp(v).Normalize(); result = TryRunCounted(in w, requestedDigits, number.DigitsMut, out length, out decimalExponent); } if (result) { Debug.Assert(requestedDigits == -1 || length == requestedDigits); number.Scale = length + decimalExponent; number.DigitsMut[length] = (byte)'\0'; number.DigitsCount = length; } return(result); }
private static Boolean HexNumberToInt32(ref NumberBuffer number, ref Int32 value) { UInt32 passedValue = 0; Boolean returnValue = HexNumberToUInt32(ref number, ref passedValue); value = (Int32)passedValue; return(returnValue); }
private unsafe static Boolean HexNumberToUInt32(ref NumberBuffer number, ref UInt32 value) { Int32 i = number.scale; if (i > UInt32Precision || i < number.precision) { return(false); } Char *p = number.digits; Debug.Assert(p != null, ""); UInt32 n = 0; while (--i >= 0) { if (n > ((UInt32)0xFFFFFFFF / 16)) { return(false); } n *= 16; if (*p != '\0') { UInt32 newN = n; if (*p != '\0') { if (*p >= '0' && *p <= '9') { newN += (UInt32)(*p - '0'); } else { if (*p >= 'A' && *p <= 'F') { newN += (UInt32)((*p - 'A') + 10); } else { Debug.Assert(*p >= 'a' && *p <= 'f', ""); newN += (UInt32)((*p - 'a') + 10); } } p++; } // Detect an overflow here... if (newN < n) { return(false); } n = newN; } } value = n; return(true); }
/// <summary> /// Parses a Decimal at the start of a Utf8 string. /// </summary> /// <param name="text">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 bool TryParse(ReadOnlySpan <byte> text, 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(System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed)); } NumberBuffer number = default; if (!TryParseNumber(text, 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.NumberBufferToDecimal(ref number, ref value)) { value = default; bytesConsumed = 0; return(false); } return(true); }
private unsafe static Boolean HexNumberToUInt32(ref NumberBuffer number, ref UInt32 value) { Int32 i = number.scale; if (i > UINT32_PRECISION || i < number.precision) { return false; } Char* p = number.digits; Debug.Assert(p != null, ""); UInt32 n = 0; while (--i >= 0) { if (n > ((UInt32)0xFFFFFFFF / 16)) { return false; } n *= 16; if (*p != '\0') { UInt32 newN = n; if (*p != '\0') { if (*p >= '0' && *p <= '9') { newN += (UInt32)(*p - '0'); } else { if (*p >= 'A' && *p <= 'F') { newN += (UInt32)((*p - 'A') + 10); } else { Debug.Assert(*p >= 'a' && *p <= 'f', ""); newN += (UInt32)((*p - 'a') + 10); } } p++; } // Detect an overflow here... if (newN < n) { return false; } n = newN; } } value = n; return true; }
// // Attempt to parse the regular floating points (the ones without names like "Infinity" and "NaN") // private static bool TryParseNormalAsFloatingPoint(ReadOnlySpan <byte> text, out double value, 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(System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed)); } NumberBuffer number = default; if (!TryParseNumber(text, ref number, out bytesConsumed, options, out bool textUsedExponentNotation)) { value = default; return(false); } if ((!textUsedExponentNotation) && (standardFormat == 'E' || standardFormat == 'e')) { value = default; bytesConsumed = 0; return(false); } if (number.Digits[0] == 0) { number.IsNegative = false; } if (!Number.NumberBufferToDouble(ref number, out value)) { value = default; bytesConsumed = 0; return(false); } return(true); }
// // This method is copied directly from CoreRT (which is in turn a C#-ized version of the CoreCLR C++ code.) // public static void RoundNumber(ref NumberBuffer number, int pos) { number.CheckConsistency(); Span <byte> digits = number.Digits; int i = 0; while (i < pos && digits[i] != 0) { i++; } if (i == pos && digits[i] >= (byte)'5') { while (i > 0 && digits[i - 1] == (byte)'9') { i--; } if (i > 0) { digits[i - 1]++; } else { number.Scale++; digits[0] = (byte)'1'; i = 1; } } else { while (i > 0 && digits[i - 1] == (byte)'0') { i--; } } if (i == 0) { number.Scale = 0; if (number.Kind == NumberBufferKind.Integer) { number.IsNegative = false; } } digits[i] = 0; number.CheckConsistency(); }
internal unsafe static Int32 ParseInt32(String s, NumberFormatInfo info) { Byte * numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes]; NumberBuffer number = new NumberBuffer(numberBufferBytes); Int32 i = 0; StringToNumber(s, ref number, info, false); if (!NumberToInt32(ref number, ref i)) { throw new OverflowException("SR.Overflow_Int32"); } return(i); }
private static unsafe void RoundNumber(ref NumberBuffer number, int pos, bool isCorrectlyRounded) { byte *dig = number.GetDigitsPointer(); int i = 0; while (i < pos && dig[i] != (byte)'\0') { i++; } if ((i == pos) && ShouldRoundUp(dig, i, isCorrectlyRounded)) { while (i > 0 && dig[i - 1] == (byte)'9') { i--; } if (i > 0) { dig[i - 1]++; } else { number.Scale++; dig[0] = (byte)('1'); i = 1; } } else { while (i > 0 && dig[i - 1] == (byte)'0') { i--; } } if (i == 0) { number.Scale = 0; // Decimals with scale ('0.00') should be rounded. } dig[i] = (byte)('\0'); number.DigitsCount = i; }
private static unsafe bool HexNumberToUInt32(ref NumberBuffer number, ref uint value) { int scale = number.scale; if ((scale > 10) || (scale < number.precision)) { return false; } char* digits = number.digits; uint num2 = 0; while (--scale >= 0) { if (num2 > 0xfffffff) { return false; } num2 *= 0x10; if (digits[0] != '\0') { uint num3 = num2; if (digits[0] != '\0') { if ((digits[0] >= '0') && (digits[0] <= '9')) { num3 += digits[0] - '0'; } else if ((digits[0] >= 'A') && (digits[0] <= 'F')) { num3 += (uint) ((digits[0] - 'A') + 10); } else { num3 += (uint) ((digits[0] - 'a') + 10); } digits++; } if (num3 < num2) { return false; } num2 = num3; } } value = num2; return true; }
// // Convert a Number to a double. // internal static bool NumberBufferToDouble(ref NumberBuffer number, out double value) { double d = NumberToDouble(ref number); if (!Double.IsFinite(d)) { value = default; return(false); } if (d == 0.0) { // normalize -0.0 to 0.0 d = 0.0; } value = d; return(true); }
private unsafe static void StringToNumber(String str, ref NumberBuffer number, NumberFormatInfo info, Boolean parseDecimal) { if (str == null) { throw new ArgumentNullException(nameof(String)); } Contract.EndContractBlock(); Debug.Assert(info != null, ""); fixed(char *stringPointer = str) { char *p = stringPointer; if (!ParseNumber(ref p, ref number, null, info, parseDecimal) || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) { throw new FormatException("SR.Format_InvalidString"); } } }
private unsafe static Boolean NumberToInt32(ref NumberBuffer number, ref Int32 value) { Int32 i = number.scale; if (i > Int32Precision || i < number.precision) { return(false); } char *p = number.digits; Debug.Assert(p != null, ""); Int32 n = 0; while (--i >= 0) { if ((UInt32)n > (0x7FFFFFFF / 10)) { return(false); } n *= 10; if (*p != '\0') { n += (Int32)(*p++ - '0'); } } if (number.sign) { n = -n; if (n > 0) { return(false); } } else { if (n < 0) { return(false); } } value = n; return(true); }
// // Convert a Number to a double. // internal static bool NumberBufferToDouble(ref NumberBuffer number, out double value) { double d = NumberToDouble(ref number); uint e = DoubleHelper.Exponent(d); ulong m = DoubleHelper.Mantissa(d); if (e == 0x7FF) { value = default; return(false); } if (e == 0 && m == 0) { d = 0; } value = d; return(true); }
private static unsafe void ConvertIntegerToString(byte *dest, ref int destIndex, int destLength, long value, FormatOptions options) { var basis = options.GetBase(); if (basis < 2 || basis > 36) { return; } // Calculate the full length (including zero padding) int length = 0; var tmp = value; do { tmp /= basis; length++; } while (tmp != 0); // Write the characters for the numbers to a temp buffer byte *tmpBuffer = stackalloc byte[length + 1]; tmp = value; int tmpIndex = length - 1; do { tmpBuffer[tmpIndex--] = ValueToIntegerChar((int)(tmp % basis), options.Uppercase); tmp /= basis; } while (tmp != 0); tmpBuffer[length] = 0; var numberBuffer = new NumberBuffer(NumberBufferKind.Integer, tmpBuffer, length, length, value < 0); FormatNumber(dest, ref destIndex, destLength, ref numberBuffer, options.Specifier, options); }
public static void DecimalToNumber(decimal value, ref NumberBuffer number) { ref MutableDecimal d = ref Unsafe.As <decimal, MutableDecimal>(ref value);
internal static unsafe bool TryParseUInt64(string s, NumberStyles style, NumberFormatInfo info, out ulong result) { byte* stackBuffer = stackalloc byte[0x72]; NumberBuffer number = new NumberBuffer(stackBuffer); result = 0L; if (!TryStringToNumber(s, style, ref number, info, false)) { return false; } if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None) { if (!HexNumberToUInt64(ref number, ref result)) { return false; } } else if (!NumberToUInt64(ref number, ref result)) { return false; } return true; }
internal static unsafe bool TryParseDouble(string value, NumberStyles options, NumberFormatInfo numfmt, out double result) { byte* stackBuffer = stackalloc byte[0x72]; NumberBuffer number = new NumberBuffer(stackBuffer); result = 0.0; if (!TryStringToNumber(value, options, ref number, numfmt, false)) { return false; } if (!NumberBufferToDouble(number.PackForNative(), ref result)) { return false; } return true; }
internal static unsafe ulong ParseUInt64(string value, NumberStyles options, NumberFormatInfo numfmt) { byte* stackBuffer = stackalloc byte[0x72]; NumberBuffer number = new NumberBuffer(stackBuffer); ulong num = 0L; StringToNumber(value, options, ref number, numfmt, false); if ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None) { if (!HexNumberToUInt64(ref number, ref num)) { throw new OverflowException(Environment.GetResourceString("Overflow_UInt64")); } return num; } if (!NumberToUInt64(ref number, ref num)) { throw new OverflowException(Environment.GetResourceString("Overflow_UInt64")); } return num; }
private static bool TryFormatDecimalF(ref 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 unsafe bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal) { string currencyDecimalSeparator; string currencyGroupSeparator; char* chPtr2; number.scale = 0; number.sign = false; string currencySymbol = null; string ansiCurrencySymbol = null; string numberDecimalSeparator = null; string numberGroupSeparator = null; bool flag = false; if ((options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None) { currencySymbol = numfmt.CurrencySymbol; if (numfmt.ansiCurrencySymbol != null) { ansiCurrencySymbol = numfmt.ansiCurrencySymbol; } numberDecimalSeparator = numfmt.NumberDecimalSeparator; numberGroupSeparator = numfmt.NumberGroupSeparator; currencyDecimalSeparator = numfmt.CurrencyDecimalSeparator; currencyGroupSeparator = numfmt.CurrencyGroupSeparator; flag = true; } else { currencyDecimalSeparator = numfmt.NumberDecimalSeparator; currencyGroupSeparator = numfmt.NumberGroupSeparator; } int num = 0; bool flag2 = false; bool flag3 = sb != null; bool flag4 = flag3 && ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None); int num2 = flag3 ? 0x7fffffff : 50; char* p = str; char ch = p[0]; while (true) { if ((!IsWhite(ch) || ((options & NumberStyles.AllowLeadingWhite) == NumberStyles.None)) || (((num & 1) != 0) && (((num & 1) == 0) || (((num & 0x20) == 0) && (numfmt.numberNegativePattern != 2))))) { if ((flag2 = ((options & NumberStyles.AllowLeadingSign) != NumberStyles.None) && ((num & 1) == 0)) && ((chPtr2 = MatchChars(p, numfmt.positiveSign)) != null)) { num |= 1; p = chPtr2 - 1; } else if (flag2 && ((chPtr2 = MatchChars(p, numfmt.negativeSign)) != null)) { num |= 1; number.sign = true; p = chPtr2 - 1; } else if (((ch == '(') && ((options & NumberStyles.AllowParentheses) != NumberStyles.None)) && ((num & 1) == 0)) { num |= 3; number.sign = true; } else { if (((currencySymbol == null) || ((chPtr2 = MatchChars(p, currencySymbol)) == null)) && ((ansiCurrencySymbol == null) || ((chPtr2 = MatchChars(p, ansiCurrencySymbol)) == null))) { break; } num |= 0x20; currencySymbol = null; ansiCurrencySymbol = null; p = chPtr2 - 1; } } ch = *(++p); } int num3 = 0; int index = 0; while (true) { if (((ch >= '0') && (ch <= '9')) || (((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None) && (((ch >= 'a') && (ch <= 'f')) || ((ch >= 'A') && (ch <= 'F'))))) { num |= 4; if (((ch != '0') || ((num & 8) != 0)) || flag4) { if (num3 < num2) { if (flag3) { sb.Append(ch); } else { number.digits[num3++] = ch; } if ((ch != '0') || parseDecimal) { index = num3; } } if ((num & 0x10) == 0) { number.scale++; } num |= 8; } else if ((num & 0x10) != 0) { number.scale--; } } else if ((((options & NumberStyles.AllowDecimalPoint) != NumberStyles.None) && ((num & 0x10) == 0)) && (((chPtr2 = MatchChars(p, currencyDecimalSeparator)) != null) || ((flag && ((num & 0x20) == 0)) && ((chPtr2 = MatchChars(p, numberDecimalSeparator)) != null)))) { num |= 0x10; p = chPtr2 - 1; } else { if (((((options & NumberStyles.AllowThousands) == NumberStyles.None) || ((num & 4) == 0)) || ((num & 0x10) != 0)) || (((chPtr2 = MatchChars(p, currencyGroupSeparator)) == null) && ((!flag || ((num & 0x20) != 0)) || ((chPtr2 = MatchChars(p, numberGroupSeparator)) == null)))) { break; } p = chPtr2 - 1; } ch = *(++p); } bool flag5 = false; number.precision = index; if (flag3) { sb.Append('\0'); } else { number.digits[index] = '\0'; } if ((num & 4) != 0) { if (((ch == 'E') || (ch == 'e')) && ((options & NumberStyles.AllowExponent) != NumberStyles.None)) { char* chPtr3 = p; ch = *(++p); chPtr2 = MatchChars(p, numfmt.positiveSign); if (chPtr2 != null) { ch = *(p = chPtr2); } else { chPtr2 = MatchChars(p, numfmt.negativeSign); if (chPtr2 != null) { ch = *(p = chPtr2); flag5 = true; } } if ((ch >= '0') && (ch <= '9')) { int num5 = 0; do { num5 = (num5 * 10) + (ch - '0'); ch = *(++p); if (num5 > 0x3e8) { num5 = 0x270f; while ((ch >= '0') && (ch <= '9')) { ch = *(++p); } } } while ((ch >= '0') && (ch <= '9')); if (flag5) { num5 = -num5; } number.scale += num5; } else { p = chPtr3; ch = p[0]; } } while (true) { if (!IsWhite(ch) || ((options & NumberStyles.AllowTrailingWhite) == NumberStyles.None)) { if ((flag2 = ((options & NumberStyles.AllowTrailingSign) != NumberStyles.None) && ((num & 1) == 0)) && ((chPtr2 = MatchChars(p, numfmt.positiveSign)) != null)) { num |= 1; p = chPtr2 - 1; } else if (flag2 && ((chPtr2 = MatchChars(p, numfmt.negativeSign)) != null)) { num |= 1; number.sign = true; p = chPtr2 - 1; } else if ((ch == ')') && ((num & 2) != 0)) { num &= -3; } else { if (((currencySymbol == null) || ((chPtr2 = MatchChars(p, currencySymbol)) == null)) && ((ansiCurrencySymbol == null) || ((chPtr2 = MatchChars(p, ansiCurrencySymbol)) == null))) { break; } currencySymbol = null; ansiCurrencySymbol = null; p = chPtr2 - 1; } } ch = *(++p); } if ((num & 2) == 0) { if ((num & 8) == 0) { if (!parseDecimal) { number.scale = 0; } if ((num & 0x10) == 0) { number.sign = false; } } str = p; return true; } } str = p; return false; }
internal static unsafe int ParseInt32(string s, NumberStyles style, NumberFormatInfo info) { byte* stackBuffer = stackalloc byte[0x72]; NumberBuffer number = new NumberBuffer(stackBuffer); int num = 0; StringToNumber(s, style, ref number, info, false); if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None) { if (!HexNumberToInt32(ref number, ref num)) { throw new OverflowException(Environment.GetResourceString("Overflow_Int32")); } return num; } if (!NumberToInt32(ref number, ref num)) { throw new OverflowException(Environment.GetResourceString("Overflow_Int32")); } return num; }
/// <summary> /// Formats a Decimal as a UTF8 string. /// </summary> /// <param name="value">Value to format</param> /// <param name="buffer">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 bool TryFormat(decimal value, Span <byte> buffer, 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); } NumberBuffer number = default; Number.DecimalToNumber(value, ref number); if (number.Digits[0] == 0) { number.IsNegative = false; // For Decimals, -0 must print as normal 0. } bool success = TryFormatDecimalG(ref number, buffer, 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.NumDigits; if (numDigits != 0 && number.Scale == numDigits && digits[numDigits - 1] == '0') { while (numDigits != 0 && digits[numDigits - 1] == '0') { digits[numDigits - 1] = 0; numDigits--; } number.CheckConsistency(); byte[] buffer2 = new byte[buffer.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(buffer[i] == buffer2[i]); } } } #endif // DEBUG return(success); } case 'f': case 'F': { NumberBuffer number = default; Number.DecimalToNumber(value, ref number); byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)2 : format.Precision; Number.RoundNumber(ref number, number.Scale + precision); 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, buffer, out bytesWritten, precision)); } case 'e': case 'E': { NumberBuffer number = default; Number.DecimalToNumber(value, ref number); byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)6 : format.Precision; Number.RoundNumber(ref number, precision + 1); 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, buffer, out bytesWritten, precision, exponentSymbol: (byte)format.Symbol)); } default: return(ThrowHelper.TryFormatThrowFormatException(out bytesWritten)); } }
private static bool TryParseNumber(ReadOnlySpan <byte> text, ref NumberBuffer number, out int bytesConsumed, ParseNumberOptions options, out bool textUsedExponentNotation) { Debug.Assert(number.Digits[0] == 0 && number.Scale == 0 && !number.IsNegative, "Number not initialized to default(NumberBuffer)"); textUsedExponentNotation = false; if (text.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 = text[srcIndex]; switch (c) { case Utf8Constants.Minus: number.IsNegative = true; goto case Utf8Constants.Plus; case Utf8Constants.Plus: srcIndex++; if (srcIndex == text.Length) { bytesConsumed = 0; return(false); } c = text[srcIndex]; break; default: break; } int startIndexDigitsBeforeDecimal = srcIndex; // Throw away any leading zeroes while (srcIndex != text.Length) { c = text[srcIndex]; if (c != '0') { break; } srcIndex++; } if (srcIndex == text.Length) { digits[0] = 0; number.Scale = 0; bytesConsumed = srcIndex; number.CheckConsistency(); return(true); } int startIndexNonLeadingDigitsBeforeDecimal = srcIndex; while (srcIndex != text.Length) { c = text[srcIndex]; if ((c - 48u) > 9) { break; } srcIndex++; } int numDigitsBeforeDecimal = srcIndex - startIndexDigitsBeforeDecimal; int numNonLeadingDigitsBeforeDecimal = srcIndex - startIndexNonLeadingDigitsBeforeDecimal; Debug.Assert(dstIndex == 0); int numNonLeadingDigitsBeforeDecimalToCopy = Math.Min(numNonLeadingDigitsBeforeDecimal, NumberBuffer.BufferSize - 1); text.Slice(startIndexNonLeadingDigitsBeforeDecimal, numNonLeadingDigitsBeforeDecimalToCopy).CopyTo(digits); dstIndex = numNonLeadingDigitsBeforeDecimalToCopy; number.Scale = numNonLeadingDigitsBeforeDecimal; if (srcIndex == text.Length) { 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 != text.Length) { c = text[srcIndex]; if ((c - 48u) > 9) { break; } srcIndex++; } 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 && text[startIndexOfDigitsAfterDecimalToCopy] == '0') { number.Scale--; startIndexOfDigitsAfterDecimalToCopy++; } } int numDigitsAfterDecimalToCopy = Math.Min(srcIndex - startIndexOfDigitsAfterDecimalToCopy, NumberBuffer.BufferSize - dstIndex - 1); text.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 == text.Length) { if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0) { // For compatibility. You can say "5." and ".5" but you can't say "." bytesConsumed = 0; return(false); } bytesConsumed = srcIndex; number.CheckConsistency(); return(true); } } if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0) { bytesConsumed = 0; return(false); } if ((c & ~0x20u) != 'E') { 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 == text.Length) { bytesConsumed = 0; return(false); } bool exponentIsNegative = false; c = text[srcIndex]; switch (c) { case Utf8Constants.Minus: exponentIsNegative = true; goto case Utf8Constants.Plus; case Utf8Constants.Plus: srcIndex++; if (srcIndex == text.Length) { bytesConsumed = 0; return(false); } c = text[srcIndex]; break; default: break; } if (!Utf8Parser.TryParseUInt32D(text.Slice(srcIndex), out uint absoluteExponent, out int bytesConsumedByExponent)) { bytesConsumed = 0; return(false); } 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) { bytesConsumed = 0; return(false); } number.Scale += (int)absoluteExponent; } bytesConsumed = srcIndex; number.CheckConsistency(); return(true); }
private unsafe static Boolean ParseNumber(ref char *str, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, Boolean parseDecimal) { const Int32 StateSign = 0x0001; const Int32 StateParens = 0x0002; const Int32 StateDigits = 0x0004; const Int32 StateNonZero = 0x0008; const Int32 StateDecimal = 0x0010; const Int32 StateCurrency = 0x0020; number.scale = 0; number.sign = false; string currSymbol = null; // currency symbol from NumberFormatInfo. Int32 state = 0; Boolean bigNumber = (sb != null); // When a StringBuilder is provided then we use it in place of the number.digits char[50] Int32 maxParseDigits = bigNumber ? Int32.MaxValue : NumberMaxDigits; char *p = str; char ch = *p; char *next; while (true) { // Eat whitespace unless we've found a sign which isn't followed by a currency symbol. // "-Kr 1231.47" is legal but "- 1231.47" is not. if (!IsWhite(ch) || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2))) { if (((state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true)))) { state |= StateSign; p = next - 1; } else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) { state |= StateCurrency; currSymbol = null; // We already found the currency symbol. There should not be more currency symbols. Set // currSymbol to NULL so that we won't search it again in the later code path. p = next - 1; } else { break; } } ch = *++p; } Int32 digCount = 0; Int32 digEnd = 0; while (true) { if ((ch >= '0' && ch <= '9')) { state |= StateDigits; if (ch != '0' || (state & StateNonZero) != 0) { if (digCount < maxParseDigits) { if (bigNumber) { sb.Append(ch); } else { number.digits[digCount++] = ch; } if (ch != '0' || parseDecimal) { digEnd = digCount; } } if ((state & StateDecimal) == 0) { number.scale++; } state |= StateNonZero; } else if ((state & StateDecimal) != 0) { number.scale--; } } else { break; } ch = *++p; } number.precision = digEnd; if (bigNumber) { sb.Append('\0'); } else { number.digits[digEnd] = '\0'; } if ((state & StateDigits) != 0) { while (true) { if (!IsWhite(ch)) { if ((((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true)))) { state |= StateSign; p = next - 1; } else if (ch == ')' && ((state & StateParens) != 0)) { state &= ~StateParens; } else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) { currSymbol = null; p = next - 1; } else { break; } } ch = *++p; } if ((state & StateParens) == 0) { if ((state & StateNonZero) == 0) { if (!parseDecimal) { number.scale = 0; } if ((state & StateDecimal) == 0) { number.sign = false; } } str = p; return(true); } } str = p; return(false); }
private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number) { Debug.Assert(precision > 0 && precision < 40); number.precision = precision; if (DoubleHelper.Exponent(value) == 0x7ff) { number.scale = DoubleHelper.Mantissa(value) != 0 ? SCALE_NAN : SCALE_INF; number.sign = DoubleHelper.Sign(value); number.digits[0] = '\0'; return; } byte *tempBuffer = stackalloc byte[MAX_BUFFER_SIZE]; char *dst = number.digits; number.scale = 0; number.sign = false; *dst = '\0'; if (value < 0.0) { number.sign = true; } if (value == 0.0) { for (int j = 0; j < precision; j++) { dst[j] = '0'; } dst[precision] = '\0'; return; } // // Get the number formatted as a string in the form x.xxxxxxexxxx // // "%.40e" byte *format = stackalloc byte[6]; format[0] = (byte)'%'; format[1] = (byte)'.'; format[2] = (byte)'4'; format[3] = (byte)'0'; format[4] = (byte)'e'; format[5] = 0; int tempBufferLength = Interop.Sys.DoubleToString(value, format, tempBuffer, MAX_BUFFER_SIZE); Debug.Assert(tempBufferLength > 0 && MAX_BUFFER_SIZE > tempBufferLength); // // Calculate the exponent value // int exponentIndex = tempBufferLength - 1; while (tempBuffer[exponentIndex] != (byte)'e' && exponentIndex > 0) { exponentIndex--; } Debug.Assert(exponentIndex > 0 && (exponentIndex < tempBufferLength - 1)); int i = exponentIndex + 1; int exponentSign = 1; if (tempBuffer[i] == '-') { exponentSign = -1; i++; } else if (tempBuffer[i] == '+') { i++; } int exponentValue = 0; while (i < tempBufferLength) { Debug.Assert(tempBuffer[i] >= (byte)'0' && tempBuffer[i] <= (byte)'9'); exponentValue = exponentValue * 10 + (tempBuffer[i] - (byte)'0'); i++; } exponentValue *= exponentSign; // // Determine decimal location. // if (exponentValue == 0) { number.scale = 1; } else { number.scale = exponentValue + 1; } // // Copy the string from the temp buffer upto precision characters, removing the sign, and decimal as required. // i = 0; int mantissaIndex = 0; while (i < precision && mantissaIndex < exponentIndex) { if (tempBuffer[mantissaIndex] >= (byte)'0' && tempBuffer[mantissaIndex] <= (byte)'9') { dst[i] = (char)tempBuffer[mantissaIndex]; i++; } mantissaIndex++; } while (i < precision) { dst[i] = '0'; // append zeros as needed i++; } dst[i] = '\0'; // // Round if needed // if (mantissaIndex >= exponentIndex || tempBuffer[mantissaIndex] < (byte)'5') { return; // rounding is not needed } i = precision - 1; while (dst[i] == '9' && i > 0) { dst[i] = '0'; i--; } if (i == 0 && dst[i] == '9') { dst[i] = '1'; number.scale++; } else { dst[i]++; } }
private static unsafe void Dragon4(double value, int precision, ref NumberBuffer number) { const double Log10V2 = 0.30102999566398119521373889472449; // DriftFactor = 1 - Log10V2 - epsilon (a small number account for drift of floating point multiplication) const double DriftFactor = 0.69; // ======================================================================================================================================== // This implementation is based on the paper: https://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf // Besides the paper, some of the code and ideas are modified from http://www.ryanjuckett.com/programming/printing-floating-point-numbers/ // You must read these two materials to fully understand the code. // // Note: we only support fixed format input. // ======================================================================================================================================== // // Overview: // // The input double number can be represented as: // value = f * 2^e = r / s. // // f: the output mantissa. Note: f is not the 52 bits mantissa of the input double number. // e: biased exponent. // r: numerator. // s: denominator. // k: value = d0.d1d2 . . . dn * 10^k // Step 1: // Extract meta data from the input double value. // // Refer to IEEE double precision floating point format. ulong f = (ulong)(ExtractFractionAndBiasedExponent(value, out int e)); int mantissaHighBitIndex = (e == -1074) ? (int)(BigInteger.LogBase2(f)) : 52; // Step 2: // Estimate k. We'll verify it and fix any error later. // // This is an improvement of the estimation in the original paper. // Inspired by http://www.ryanjuckett.com/programming/printing-floating-point-numbers/ // // LOG10V2 = 0.30102999566398119521373889472449 // DRIFT_FACTOR = 0.69 = 1 - log10V2 - epsilon (a small number account for drift of floating point multiplication) int k = (int)(Math.Ceiling(((mantissaHighBitIndex + e) * Log10V2) - DriftFactor)); // Step 3: // Store the input double value in BigInteger format. // // To keep the precision, we represent the double value as r/s. // We have several optimization based on following table in the paper. // // ---------------------------------------------------------------------------------------------------------- // | e >= 0 | e < 0 | // ---------------------------------------------------------------------------------------------------------- // | f != b^(P - 1) | f = b^(P - 1) | e = min exp or f != b^(P - 1) | e > min exp and f = b^(P - 1) | // -------------------------------------------------------------------------------------------------------------- // | r | f * b^e * 2 | f * b^(e + 1) * 2 | f * 2 | f * b * 2 | // -------------------------------------------------------------------------------------------------------------- // | s | 2 | b * 2 | b^(-e) * 2 | b^(-e + 1) * 2 | // -------------------------------------------------------------------------------------------------------------- // // Note, we do not need m+ and m- because we only support fixed format input here. // m+ and m- are used for free format input, which need to determine the exact range of values // that would round to value when input so that we can generate the shortest correct number.digits. // // In our case, we just output number.digits until reaching the expected precision. var r = new BigInteger(f); var s = new BigInteger(0); if (e >= 0) { // When f != b^(P - 1): // r = f * b^e * 2 // s = 2 // value = r / s = f * b^e * 2 / 2 = f * b^e / 1 // // When f = b^(P - 1): // r = f * b^(e + 1) * 2 // s = b * 2 // value = r / s = f * b^(e + 1) * 2 / b * 2 = f * b^e / 1 // // Therefore, we can simply say that when e >= 0: // r = f * b^e = f * 2^e // s = 1 r.ShiftLeft((uint)(e)); s.SetUInt64(1); } else { // When e = min exp or f != b^(P - 1): // r = f * 2 // s = b^(-e) * 2 // value = r / s = f * 2 / b^(-e) * 2 = f / b^(-e) // // When e > min exp and f = b^(P - 1): // r = f * b * 2 // s = b^(-e + 1) * 2 // value = r / s = f * b * 2 / b^(-e + 1) * 2 = f / b^(-e) // // Therefore, we can simply say that when e < 0: // r = f // s = b^(-e) = 2^(-e) BigInteger.ShiftLeft(1, (uint)(-e), ref s); } // According to the paper, we should use k >= 0 instead of k > 0 here. // However, if k = 0, both r and s won't be changed, we don't need to do any operation. // // Following are the Scheme code from the paper: // -------------------------------------------------------------------------------- // (if (>= est 0) // (fixup r (* s (exptt B est)) m+ m− est B low-ok? high-ok? ) // (let ([scale (exptt B (− est))]) // (fixup (* r scale) s (* m+ scale) (* m− scale) est B low-ok? high-ok? )))) // -------------------------------------------------------------------------------- // // If est is 0, (* s (exptt B est)) = s, (* r scale) = (* r (exptt B (− est)))) = r. // // So we just skip when k = 0. if (k > 0) { s.MultiplyPow10((uint)(k)); } else if (k < 0) { r.MultiplyPow10((uint)(-k)); } if (BigInteger.Compare(ref r, ref s) >= 0) { // The estimation was incorrect. Fix the error by increasing 1. k += 1; } else { r.Multiply10(); } number.Scale = (k - 1); // This the prerequisite of calling BigInteger.HeuristicDivide(). BigInteger.PrepareHeuristicDivide(ref r, ref s); // Step 4: // Calculate number.digits. // // Output number.digits until reaching the last but one precision or the numerator becomes zero. int digitsNum = 0; int currentDigit = 0; while (true) { currentDigit = (int)(BigInteger.HeuristicDivide(ref r, ref s)); if (r.IsZero() || ((digitsNum + 1) == precision)) { break; } number.Digits[digitsNum] = (char)('0' + currentDigit); digitsNum++; r.Multiply10(); } // Step 5: // Set the last digit. // // We round to the closest digit by comparing value with 0.5: // compare( value, 0.5 ) // = compare( r / s, 0.5 ) // = compare( r, 0.5 * s) // = compare(2 * r, s) // = compare(r << 1, s) r.ShiftLeft(1); int compareResult = BigInteger.Compare(ref r, ref s); bool isRoundDown = compareResult < 0; // We are in the middle, round towards the even digit (i.e. IEEE rouding rules) if (compareResult == 0) { isRoundDown = (currentDigit & 1) == 0; } if (isRoundDown) { number.Digits[digitsNum] = (char)('0' + currentDigit); digitsNum++; } else { char *pCurrentDigit = (number.GetDigitsPointer() + digitsNum); // Rounding up for 9 is special. if (currentDigit == 9) { // find the first non-nine prior digit while (true) { // If we are at the first digit if (pCurrentDigit == number.GetDigitsPointer()) { // Output 1 at the next highest exponent *pCurrentDigit = '1'; digitsNum++; number.Scale += 1; break; } pCurrentDigit--; digitsNum--; if (*pCurrentDigit != '9') { // increment the digit *pCurrentDigit += (char)(1); digitsNum++; break; } } } else { // It's simple if the digit is not 9. *pCurrentDigit = (char)('0' + currentDigit + 1); digitsNum++; } } while (digitsNum < precision) { number.Digits[digitsNum] = '0'; digitsNum++; } number.Digits[precision] = '\0'; number.Scale++; number.Sign = double.IsNegative(value); }
private static unsafe int GetLengthForFormatGeneral(ref NumberBuffer number, int nMaxDigits) { // NOTE: Must be kept in sync with FormatGeneral! int length = 0; int scale = number.Scale; int digPos = scale; bool scientific = false; // Don't switch to scientific notation if (digPos > nMaxDigits || digPos < -3) { digPos = 1; scientific = true; } byte *dig = number.GetDigitsPointer(); if (number.IsNegative) { length++; // (byte)'-'; } if (digPos > 0) { do { if (*dig != 0) { dig++; } length++; } while (--digPos > 0); } else { length++; } if (*dig != 0 || digPos < 0) { length++; // (byte)'.'; while (digPos < 0) { length++; // (byte)'0'; digPos++; } while (*dig != 0) { length++; // *dig++; dig++; } } if (scientific) { length++; // e or E int exponent = number.Scale - 1; if (exponent >= 0) { length++; } length += GetLengthIntegerToString(exponent, 10, 2); } return(length); }
private static unsafe void FormatGeneral(byte *dest, ref int destIndex, int destLength, ref NumberBuffer number, int nMaxDigits, byte expChar) { int scale = number.Scale; int digPos = scale; bool scientific = false; // Don't switch to scientific notation if (digPos > nMaxDigits || digPos < -3) { digPos = 1; scientific = true; } byte *dig = number.GetDigitsPointer(); if (number.IsNegative) { if (destIndex >= destLength) { return; } dest[destIndex++] = (byte)'-'; } if (digPos > 0) { do { if (destIndex >= destLength) { return; } dest[destIndex++] = (*dig != 0) ? (byte)(*dig++) : (byte)'0'; } while (--digPos > 0); } else { if (destIndex >= destLength) { return; } dest[destIndex++] = (byte)'0'; } if (*dig != 0 || digPos < 0) { if (destIndex >= destLength) { return; } dest[destIndex++] = (byte)'.'; while (digPos < 0) { if (destIndex >= destLength) { return; } dest[destIndex++] = (byte)'0'; digPos++; } while (*dig != 0) { if (destIndex >= destLength) { return; } dest[destIndex++] = *dig++; } } if (scientific) { if (destIndex >= destLength) { return; } dest[destIndex++] = expChar; int exponent = number.Scale - 1; var exponentFormatOptions = new FormatOptions(NumberFormatKind.DecimalForceSigned, 0, 2, false); ConvertIntegerToString(dest, ref destIndex, destLength, exponent, exponentFormatOptions); } }
private static unsafe bool NumberToUInt64(ref NumberBuffer number, ref ulong value) { int scale = number.scale; if (((scale > 20) || (scale < number.precision)) || number.sign) { return false; } char* digits = number.digits; ulong num2 = 0L; while (--scale >= 0) { if (num2 > 0x1999999999999999L) { return false; } num2 *= (ulong) 10L; if (digits[0] != '\0') { digits++; ulong num3 = num2 + (digits[0] - '0'); if (num3 < num2) { return false; } num2 = num3; } } value = num2; return true; }
internal static unsafe decimal ParseDecimal(string value, NumberStyles options, NumberFormatInfo numfmt) { byte* stackBuffer = stackalloc byte[0x72]; NumberBuffer number = new NumberBuffer(stackBuffer); decimal num = 0M; StringToNumber(value, options, ref number, numfmt, true); if (!NumberBufferToDecimal(number.PackForNative(), ref num)) { throw new OverflowException(Environment.GetResourceString("Overflow_Decimal")); } return num; }
private static unsafe void FormatDecimalOrHexadecimal(byte *dest, ref int destIndex, int destLength, ref NumberBuffer number, int zeroPadding, bool outputPositiveSign) { if (number.IsNegative) { if (destIndex >= destLength) { return; } dest[destIndex++] = (byte)'-'; } else if (outputPositiveSign) { if (destIndex >= destLength) { return; } dest[destIndex++] = (byte)'+'; } // Zero Padding for (int i = 0; i < zeroPadding; i++) { if (destIndex >= destLength) { return; } dest[destIndex++] = (byte)'0'; } var digitCount = number.DigitsCount; byte *digits = number.GetDigitsPointer(); for (int i = 0; i < digitCount; i++) { if (destIndex >= destLength) { return; } dest[destIndex++] = digits[i]; } }
private static unsafe void FormatNumber(byte *dest, ref int destIndex, int destLength, ref NumberBuffer number, int nMaxDigits, FormatOptions options) { bool isCorrectlyRounded = (number.Kind == NumberBufferKind.Float); // If we have an integer, and the rendering is the default `G`, then use Decimal rendering which is faster if (number.Kind == NumberBufferKind.Integer && options.Kind == NumberFormatKind.General && options.Specifier == 0) { options.Kind = NumberFormatKind.Decimal; } int length; switch (options.Kind) { case NumberFormatKind.DecimalForceSigned: case NumberFormatKind.Decimal: case NumberFormatKind.Hexadecimal: length = number.DigitsCount; var zeroPadding = (int)options.Specifier; int actualZeroPadding = 0; if (length < zeroPadding) { actualZeroPadding = zeroPadding - length; length = zeroPadding; } bool outputPositiveSign = options.Kind == NumberFormatKind.DecimalForceSigned; length += number.IsNegative || outputPositiveSign ? 1 : 0; // Perform left align if (AlignLeft(dest, ref destIndex, destLength, options.AlignAndSize, length)) { return; } FormatDecimalOrHexadecimal(dest, ref destIndex, destLength, ref number, actualZeroPadding, outputPositiveSign); // Perform right align AlignRight(dest, ref destIndex, destLength, options.AlignAndSize, length); break; default: case NumberFormatKind.General: if (nMaxDigits < 1) { // This ensures that the PAL code pads out to the correct place even when we use the default precision nMaxDigits = number.DigitsCount; } RoundNumber(ref number, nMaxDigits, isCorrectlyRounded); // Calculate final rendering length length = GetLengthForFormatGeneral(ref number, nMaxDigits); // Perform left align if (AlignLeft(dest, ref destIndex, destLength, options.AlignAndSize, length)) { return; } // Format using general formatting FormatGeneral(dest, ref destIndex, destLength, ref number, nMaxDigits, options.Uppercase ? (byte)'E' : (byte)'e'); // Perform right align AlignRight(dest, ref destIndex, destLength, options.AlignAndSize, length); break; } }
internal static unsafe bool TryStringToNumber(string str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal) { if (str == null) { return false; } fixed (char* str2 = ((char*) str)) { char* chPtr = str2; char* chPtr2 = chPtr; if (!ParseNumber(ref chPtr2, options, ref number, sb, numfmt, parseDecimal) || ((((long) ((chPtr2 - chPtr) / 2)) < str.Length) && !TrailingZeros(str, (int) ((long) ((chPtr2 - chPtr) / 2))))) { return false; } } return true; }
private static unsafe void RoundNumber(ref NumberBuffer number, int pos) { char* dig = number.digits; int i = 0; while (i < pos && dig[i] != 0) i++; if (i == pos && dig[i] >= '5') { while (i > 0 && dig[i - 1] == '9') i--; if (i > 0) { dig[i - 1]++; } else { number.scale++; dig[0] = '1'; i = 1; } } else { while (i > 0 && dig[i - 1] == '0') i--; } if (i == 0) { number.scale = 0; number.sign = false; } dig[i] = '\0'; }
private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal) { const int StateSign = 0x0001; const int StateParens = 0x0002; const int StateDigits = 0x0004; const int StateNonZero = 0x0008; const int StateDecimal = 0x0010; const int StateCurrency = 0x0020; number.scale = 0; number.sign = false; string decSep; // Decimal separator from NumberFormatInfo. string groupSep; // Group separator from NumberFormatInfo. string currSymbol = null; // Currency symbol from NumberFormatInfo. bool parsingCurrency = false; if ((options & NumberStyles.AllowCurrencySymbol) != 0) { currSymbol = numfmt.CurrencySymbol; // 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. // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part). decSep = numfmt.CurrencyDecimalSeparator; groupSep = numfmt.CurrencyGroupSeparator; parsingCurrency = true; } else { decSep = numfmt.NumberDecimalSeparator; groupSep = numfmt.NumberGroupSeparator; } int state = 0; bool bigNumber = (sb != null); // When a StringBuilder is provided then we use it in place of the number.digits char[50] int maxParseDigits = bigNumber ? int.MaxValue : NumberMaxDigits; char* p = str; char ch = *p; char* next; char* dig = number.digits; while (true) { // Eat whitespace unless we've found a sign which isn't followed by a currency symbol. // "-Kr 1231.47" is legal but "- 1231.47" is not. if (!IsWhite(ch) || (options & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2))) { if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true)))) { state |= StateSign; p = next - 1; } else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0)) { state |= StateSign | StateParens; number.sign = true; } else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) { state |= StateCurrency; currSymbol = null; // We already found the currency symbol. There should not be more currency symbols. Set // currSymbol to NULL so that we won't search it again in the later code path. p = next - 1; } else { break; } } ch = *++p; } int digCount = 0; int digEnd = 0; while (true) { if ((ch >= '0' && ch <= '9') || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')))) { state |= StateDigits; if (ch != '0' || (state & StateNonZero) != 0 || (bigNumber && ((options & NumberStyles.AllowHexSpecifier) != 0))) { if (digCount < maxParseDigits) { if (bigNumber) sb.Append(ch); else dig[digCount++] = ch; if (ch != '0' || parseDecimal) { digEnd = digCount; } } if ((state & StateDecimal) == 0) { number.scale++; } state |= StateNonZero; } else if ((state & StateDecimal) != 0) { number.scale--; } } else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberDecimalSeparator)) != null)) { state |= StateDecimal; p = next - 1; } else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberGroupSeparator)) != null)) { p = next - 1; } else { break; } ch = *++p; } bool negExp = false; number.precision = digEnd; if (bigNumber) sb.Append('\0'); else dig[digEnd] = '\0'; if ((state & StateDigits) != 0) { if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0)) { char* temp = p; ch = *++p; if ((next = MatchChars(p, numfmt.PositiveSign)) != null) { ch = *(p = next); } else if ((next = MatchChars(p, numfmt.NegativeSign)) != null) { ch = *(p = next); negExp = true; } if (ch >= '0' && ch <= '9') { int exp = 0; do { exp = exp * 10 + (ch - '0'); ch = *++p; if (exp > 1000) { exp = 9999; while (ch >= '0' && ch <= '9') { ch = *++p; } } } while (ch >= '0' && ch <= '9'); if (negExp) { exp = -exp; } number.scale += exp; } else { p = temp; ch = *p; } } while (true) { if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0) { if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true)))) { state |= StateSign; p = next - 1; } else if (ch == ')' && ((state & StateParens) != 0)) { state &= ~StateParens; } else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) { currSymbol = null; p = next - 1; } else { break; } } ch = *++p; } if ((state & StateParens) == 0) { if ((state & StateNonZero) == 0) { if (!parseDecimal) { number.scale = 0; } if ((state & StateDecimal) == 0) { number.sign = false; } } str = p; return true; } } str = p; return false; }
internal static unsafe float ParseSingle(string value, NumberStyles options, NumberFormatInfo numfmt) { if (value == null) { throw new ArgumentNullException("value"); } byte* stackBuffer = stackalloc byte[0x72]; NumberBuffer number = new NumberBuffer(stackBuffer); double num = 0.0; if (!TryStringToNumber(value, options, ref number, numfmt, false)) { string str = value.Trim(); if (str.Equals(numfmt.PositiveInfinitySymbol)) { return float.PositiveInfinity; } if (str.Equals(numfmt.NegativeInfinitySymbol)) { return float.NegativeInfinity; } if (!str.Equals(numfmt.NaNSymbol)) { throw new FormatException(Environment.GetResourceString("Format_InvalidString")); } return float.NaN; } if (!NumberBufferToDouble(number.PackForNative(), ref num)) { throw new OverflowException(Environment.GetResourceString("Overflow_Single")); } float f = (float) num; if (float.IsInfinity(f)) { throw new OverflowException(Environment.GetResourceString("Overflow_Single")); } return f; }
internal static unsafe string NumberToString(NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal) { int nMinDigits = -1; StringBuilder sb = new StringBuilder(MIN_SB_BUFFER_SIZE); switch (format) { case 'C': case 'c': { nMinDigits = nMaxDigits >= 0 ? nMaxDigits : info.CurrencyDecimalDigits; if (nMaxDigits < 0) nMaxDigits = info.CurrencyDecimalDigits; RoundNumber(ref number, number.scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed. FormatCurrency(sb, number, nMinDigits, nMaxDigits, info); break; } case 'F': case 'f': { if (nMaxDigits < 0) nMaxDigits = nMinDigits = info.NumberDecimalDigits; else nMinDigits = nMaxDigits; RoundNumber(ref number, number.scale + nMaxDigits); if (number.sign) sb.Append(info.NegativeSign); FormatFixed(sb, number, nMinDigits, nMaxDigits, info, null, info.NumberDecimalSeparator, null); break; } case 'N': case 'n': { if (nMaxDigits < 0) nMaxDigits = nMinDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation else nMinDigits = nMaxDigits; RoundNumber(ref number, number.scale + nMaxDigits); FormatNumber(sb, number, nMinDigits, nMaxDigits, info); break; } case 'E': case 'e': { if (nMaxDigits < 0) nMaxDigits = nMinDigits = 6; else nMinDigits = nMaxDigits; nMaxDigits++; RoundNumber(ref number, nMaxDigits); if (number.sign) sb.Append(info.NegativeSign); FormatScientific(sb, number, nMinDigits, nMaxDigits, info, format); break; } case 'G': case 'g': { bool enableRounding = true; if (nMaxDigits < 1) { if (isDecimal && (nMaxDigits == -1)) { // Default to 29 digits precision only for G formatting without a precision specifier // This ensures that the PAL code pads out to the correct place even when we use the default precision nMaxDigits = nMinDigits = DECIMAL_PRECISION; enableRounding = false; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant } else { // This ensures that the PAL code pads out to the correct place even when we use the default precision nMaxDigits = nMinDigits = number.precision; } } else nMinDigits = nMaxDigits; if (enableRounding) // Don't round for G formatting without precision RoundNumber(ref number, nMaxDigits); // This also fixes up the minus zero case else { if (isDecimal && (number.digits[0] == 0)) { // Minus zero should be formatted as 0 number.sign = false; } } if (number.sign) sb.Append(info.NegativeSign); FormatGeneral(sb, number, nMinDigits, nMaxDigits, info, (char)(format - ('G' - 'E')), !enableRounding); break; } case 'P': case 'p': { if (nMaxDigits < 0) nMaxDigits = nMinDigits = info.PercentDecimalDigits; else nMinDigits = nMaxDigits; number.scale += 2; RoundNumber(ref number, number.scale + nMaxDigits); FormatPercent(sb, number, nMinDigits, nMaxDigits, info); break; } default: throw new FormatException(SR.Argument_BadFormatSpecifier); } return sb.ToString(); }
private static unsafe void StringToNumber(string str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal) { if (str == null) { throw new ArgumentNullException("String"); } fixed (char* str2 = ((char*) str)) { char* chPtr = str2; char* chPtr2 = chPtr; if (!ParseNumber(ref chPtr2, options, ref number, null, info, parseDecimal) || ((((long) ((chPtr2 - chPtr) / 2)) < str.Length) && !TrailingZeros(str, (int) ((long) ((chPtr2 - chPtr) / 2))))) { throw new FormatException(Environment.GetResourceString("Format_InvalidString")); } } }
private static void FormatNumber(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) { string fmt = number.sign ? s_negNumberFormats[info.NumberNegativePattern] : s_posNumberFormat; foreach (char ch in fmt) { switch (ch) { case '#': FormatFixed(sb, number, nMinDigits, nMaxDigits, info, info.NumberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator); break; case '-': sb.Append(info.NegativeSign); break; default: sb.Append(ch); break; } } }
internal static unsafe bool TryParseSingle(string value, NumberStyles options, NumberFormatInfo numfmt, out float result) { byte* stackBuffer = stackalloc byte[0x72]; NumberBuffer number = new NumberBuffer(stackBuffer); result = 0f; double num = 0.0; if (!TryStringToNumber(value, options, ref number, numfmt, false)) { return false; } if (!NumberBufferToDouble(number.PackForNative(), ref num)) { return false; } float f = (float) num; if (float.IsInfinity(f)) { return false; } result = f; return true; }
//public static unsafe void Dragon4Half(Half value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number) //{ // Half v = Half.IsNegative(value) ? Half.Negate(value) : value; // Debug.Assert((double)v > 0.0); // Debug.Assert(Half.IsFinite(v)); // ushort mantissa = ExtractFractionAndBiasedExponent(value, out int exponent); // uint mantissaHighBitIdx; // bool hasUnequalMargins = false; // if ((mantissa >> DiyFp.HalfImplicitBitIndex) != 0) // { // mantissaHighBitIdx = DiyFp.HalfImplicitBitIndex; // hasUnequalMargins = (mantissa == (1U << DiyFp.HalfImplicitBitIndex)); // } // else // { // Debug.Assert(mantissa != 0); // mantissaHighBitIdx = (uint)BitOperations.Log2(mantissa); // } // int length = (int)(Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.Digits, out int decimalExponent)); // number.Scale = decimalExponent + 1; // number.Digits[length] = (byte)('\0'); // number.DigitsCount = length; //} public static void Dragon4Single(float value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number) { var v = value.IsNegative() ? -value : value; Debug.Assert(v > 0); Debug.Assert(v.IsFinite()); var mantissa = ExtractFractionAndBiasedExponent(value, out var exponent); uint mantissaHighBitIdx; var hasUnequalMargins = false; if (mantissa >> DiyFp.SingleImplicitBitIndex != 0) { mantissaHighBitIdx = DiyFp.SingleImplicitBitIndex; hasUnequalMargins = mantissa == 1U << DiyFp.SingleImplicitBitIndex; } else { Debug.Assert(mantissa != 0); mantissaHighBitIdx = (uint)BitOperations.Log2(mantissa); } var length = (int)Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.DigitsMut, out var decimalExponent); number.Scale = decimalExponent + 1; number.DigitsMut[length] = (byte)'\0'; number.DigitsCount = length; }
internal static bool TryStringToNumber(string str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal) { return TryStringToNumber(str, options, ref number, null, numfmt, parseDecimal); }
public static unsafe bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value) { MutableDecimal d = new MutableDecimal(); byte *p = number.UnsafeDigits; int e = number.Scale; if (*p == 0) { // 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 // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.) if (e > 0) { e = 0; } } else { if (e > DECIMAL_PRECISION) { return(false); } while (((e > 0) || ((*p != 0) && (e > -28))) && ((d.High < 0x19999999) || ((d.High == 0x19999999) && ((d.Mid < 0x99999999) || ((d.Mid == 0x99999999) && ((d.Low < 0x99999999) || ((d.Low == 0x99999999) && (*p <= '5')))))))) { DecimalDecCalc.DecMul10(ref d); if (*p != 0) { DecimalDecCalc.DecAddInt32(ref d, (uint)(*p++ - '0')); } e--; } if (*p++ >= '5') { bool round = true; if ((*(p - 1) == '5') && ((*(p - 2) % 2) == 0)) { // Check if previous digit is even, only if the when we are unsure whether hows to do // Banker's rounding. For digits > 5 we will be rounding up anyway. int count = 20; // Look at the next 20 digits to check to round while ((*p == '0') && (count != 0)) { p++; count--; } if ((*p == '\0') || (count == 0)) { round = false;// Do nothing } } if (round) { DecimalDecCalc.DecAddInt32(ref d, 1); if ((d.High | d.Mid | d.Low) == 0) { // If we got here, the magnitude portion overflowed and wrapped back to 0 as the magnitude was already at the MaxValue point: // // 79,228,162,514,264,337,593,543,950,335e+X // // Manually force it to the correct result: // // 7,922,816,251,426,433,759,354,395,034e+(X+1) // // This code path can be reached by trying to parse the following as a Decimal: // // 0.792281625142643375935439503355e28 // d.High = 0x19999999; d.Mid = 0x99999999; d.Low = 0x9999999A; e++; } } } } if (e > 0) { return(false); // Rounding may have caused its own overflow. For example, parsing "0.792281625142643375935439503355e29" will get here. } if (e <= -DECIMAL_PRECISION) { // Parsing a large scale zero can give you more precision than fits in the decimal. // This should only happen for actual zeros or very small numbers that round to zero. d.High = 0; d.Low = 0; d.Mid = 0; d.Scale = DECIMAL_PRECISION - 1; } else { d.Scale = -e; } d.IsNegative = number.IsNegative; value = Unsafe.As <MutableDecimal, decimal>(ref d); return(true); }
private static unsafe void FormatGeneral(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific) { int digPos = number.scale; bool scientific = false; if (!bSuppressScientific) { // Don't switch to scientific notation if (digPos > nMaxDigits || digPos < -3) { digPos = 1; scientific = true; } } char* dig = number.digits; if (digPos > 0) { do { sb.Append((*dig != 0) ? *dig++ : '0'); } while (--digPos > 0); } else { sb.Append('0'); } if (*dig != 0 || digPos < 0) { sb.Append(info.NumberDecimalSeparator); while (digPos < 0) { sb.Append('0'); digPos++; } while (*dig != 0) sb.Append(*dig++); } if (scientific) FormatExponent(sb, info, number.scale - 1, expChar, 2, true); }
public static String FormatUInt64(ulong value, String format, IFormatProvider provider) { NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); int digits; char fmt = ParseFormatSpecifier(format, out digits); // ANDing fmt with FFDF has the effect of uppercasing the character because we've removed the bit // that marks lower-case. switch (fmt) { case 'G': case 'g': if (digits > 0) { NumberBuffer number = new NumberBuffer(); UInt64ToNumber(value, ref number); if (fmt != 0) return NumberToString(number, fmt, digits, info, false); return NumberToStringFormat(number, format, info); } // fall through goto case 'D'; case 'D': case 'd': return UInt64ToDecStr(value, digits); case 'X': case 'x': // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code // produces lowercase. return Int64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits); default: { NumberBuffer number = new NumberBuffer(); UInt64ToNumber(value, ref number); if (fmt != 0) return NumberToString(number, fmt, digits, info, false); return NumberToStringFormat(number, format, info); } } }
internal static unsafe string NumberToStringFormat(NumberBuffer number, string format, NumberFormatInfo info) { int digitCount; int decimalPos; int firstDigit; int lastDigit; int digPos; bool scientific; int thousandPos; int thousandCount = 0; bool thousandSeps; int scaleAdjust; int adjust; int section; int src; char* dig = number.digits; char ch; section = FindSection(format, dig[0] == 0 ? 2 : number.sign ? 1 : 0); while (true) { digitCount = 0; decimalPos = -1; firstDigit = 0x7FFFFFFF; lastDigit = 0; scientific = false; thousandPos = -1; thousandSeps = false; scaleAdjust = 0; src = section; fixed (char* pFormat = format) { while ((ch = pFormat[src++]) != 0 && ch != ';') { switch (ch) { case '#': digitCount++; break; case '0': if (firstDigit == 0x7FFFFFFF) firstDigit = digitCount; digitCount++; lastDigit = digitCount; break; case '.': if (decimalPos < 0) decimalPos = digitCount; break; case ',': if (digitCount > 0 && decimalPos < 0) { if (thousandPos >= 0) { if (thousandPos == digitCount) { thousandCount++; break; } thousandSeps = true; } thousandPos = digitCount; thousandCount = 1; } break; case '%': scaleAdjust += 2; break; case '\x2030': scaleAdjust += 3; break; case '\'': case '"': while (pFormat[src] != 0 && pFormat[src++] != ch) ; break; case '\\': if (pFormat[src] != 0) src++; break; case 'E': case 'e': if (pFormat[src] == '0' || ((pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0')) { while (pFormat[++src] == '0') ; scientific = true; } break; } } } if (decimalPos < 0) decimalPos = digitCount; if (thousandPos >= 0) { if (thousandPos == decimalPos) scaleAdjust -= thousandCount * 3; else thousandSeps = true; } if (dig[0] != 0) { number.scale += scaleAdjust; int pos = scientific ? digitCount : number.scale + digitCount - decimalPos; RoundNumber(ref number, pos); if (dig[0] == 0) { src = FindSection(format, 2); if (src != section) { section = src; continue; } } } else { number.sign = false; // We need to format -0 without the sign set. number.scale = 0; // Decimals with scale ('0.00') should be rounded. } break; } firstDigit = firstDigit < decimalPos ? decimalPos - firstDigit : 0; lastDigit = lastDigit > decimalPos ? decimalPos - lastDigit : 0; if (scientific) { digPos = decimalPos; adjust = 0; } else { digPos = number.scale > decimalPos ? number.scale : decimalPos; adjust = number.scale - decimalPos; } src = section; // Adjust can be negative, so we make this an int instead of an unsigned int. // Adjust represents the number of characters over the formatting e.g. format string is "0000" and you are trying to // format 100000 (6 digits). Means adjust will be 2. On the other hand if you are trying to format 10 adjust will be // -2 and we'll need to fixup these digits with 0 padding if we have 0 formatting as in this example. int[] thousandsSepPos = new int[4]; int thousandsSepCtr = -1; if (thousandSeps) { // We need to precompute this outside the number formatting loop if (info.NumberGroupSeparator.Length > 0) { // We need this array to figure out where to insert the thousands separator. We would have to traverse the string // backwards. PIC formatting always traverses forwards. These indices are precomputed to tell us where to insert // the thousands separator so we can get away with traversing forwards. Note we only have to compute up to digPos. // The max is not bound since you can have formatting strings of the form "000,000..", and this // should handle that case too. int[] groupDigits = info.NumberGroupSizes; int groupSizeIndex = 0; // Index into the groupDigits array. int groupTotalSizeCount = 0; int groupSizeLen = groupDigits.Length; // The length of groupDigits array. if (groupSizeLen != 0) groupTotalSizeCount = groupDigits[groupSizeIndex]; // The current running total of group size. int groupSize = groupTotalSizeCount; int totalDigits = digPos + ((adjust < 0) ? adjust : 0); // Actual number of digits in o/p int numDigits = (firstDigit > totalDigits) ? firstDigit : totalDigits; while (numDigits > groupTotalSizeCount) { if (groupSize == 0) break; ++thousandsSepCtr; if (thousandsSepCtr >= thousandsSepPos.Length) Array.Resize(ref thousandsSepPos, thousandsSepPos.Length * 2); thousandsSepPos[thousandsSepCtr] = groupTotalSizeCount; if (groupSizeIndex < groupSizeLen - 1) { groupSizeIndex++; groupSize = groupDigits[groupSizeIndex]; } groupTotalSizeCount += groupSize; } } } StringBuilder sb = new StringBuilder(MIN_SB_BUFFER_SIZE); if (number.sign && section == 0) sb.Append(info.NegativeSign); bool decimalWritten = false; fixed (char* pFormat = format) { char* cur = dig; while ((ch = pFormat[src++]) != 0 && ch != ';') { if (adjust > 0) { switch (ch) { case '#': case '0': case '.': while (adjust > 0) { // digPos will be one greater than thousandsSepPos[thousandsSepCtr] since we are at // the character after which the groupSeparator needs to be appended. sb.Append(*cur != 0 ? *cur++ : '0'); if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) { if (digPos == thousandsSepPos[thousandsSepCtr] + 1) { sb.Append(info.NumberGroupSeparator); thousandsSepCtr--; } } digPos--; adjust--; } break; } } switch (ch) { case '#': case '0': { if (adjust < 0) { adjust++; ch = digPos <= firstDigit ? '0' : '\0'; } else { ch = *cur != 0 ? *cur++ : digPos > lastDigit ? '0' : '\0'; } if (ch != 0) { sb.Append(ch); if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) { if (digPos == thousandsSepPos[thousandsSepCtr] + 1) { sb.Append(info.NumberGroupSeparator); thousandsSepCtr--; } } } digPos--; break; } case '.': { if (digPos != 0 || decimalWritten) { // For compatibility, don't echo repeated decimals break; } // If the format has trailing zeros or the format has a decimal and digits remain if (lastDigit < 0 || (decimalPos < digitCount && *cur != 0)) { sb.Append(info.NumberDecimalSeparator); decimalWritten = true; } break; } case '\x2030': sb.Append(info.PerMilleSymbol); break; case '%': sb.Append(info.PercentSymbol); break; case ',': break; case '\'': case '"': while (pFormat[src] != 0 && pFormat[src] != ch) sb.Append(pFormat[src++]); if (pFormat[src] != 0) src++; break; case '\\': if (pFormat[src] != 0) sb.Append(pFormat[src++]); break; case 'E': case 'e': { bool positiveSign = false; int i = 0; if (scientific) { if (pFormat[src] == '0') { // Handles E0, which should format the same as E-0 i++; } else if (pFormat[src] == '+' && pFormat[src + 1] == '0') { // Handles E+0 positiveSign = true; } else if (pFormat[src] == '-' && pFormat[src + 1] == '0') { // Handles E-0 // Do nothing, this is just a place holder s.t. we don't break out of the loop. } else { sb.Append(ch); break; } while (pFormat[++src] == '0') i++; if (i > 10) i = 10; int exp = dig[0] == 0 ? 0 : number.scale - decimalPos; FormatExponent(sb, info, exp, ch, i, positiveSign); scientific = false; } else { sb.Append(ch); // Copy E or e to output if (pFormat[src] == '+' || pFormat[src] == '-') sb.Append(pFormat[src++]); while (pFormat[src] == '0') sb.Append(pFormat[src++]); } break; } default: sb.Append(ch); break; } } } return sb.ToString(); }
private static bool TryFormatDecimalG(ref NumberBuffer number, Span <byte> destination, out int bytesWritten) { int scale = number.Scale; ReadOnlySpan <byte> digits = number.Digits; int numDigits = number.NumDigits; 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); }
internal static unsafe bool TryStringToNumber(string str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal) { if (str == null) { return false; } Debug.Assert(numfmt != null); fixed (char* stringPointer = str) { char* p = stringPointer; if (!ParseNumber(ref p, options, ref number, sb, numfmt, parseDecimal) || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) { return false; } } return true; }
private static bool TryFormatDecimalE(ref NumberBuffer number, Span <byte> buffer, 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 (buffer.Length < numBytesNeeded) { bytesWritten = 0; return(false); } int dstIndex = 0; int srcIndex = 0; if (number.IsNegative) { buffer[dstIndex++] = Utf8Constants.Minus; } // // Emit exactly one digit before the decimal point. // int exponent; byte firstDigit = digits[srcIndex]; if (firstDigit == 0) { buffer[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 { buffer[dstIndex++] = firstDigit; srcIndex++; exponent = scale - 1; } if (precision > 0) { buffer[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) { buffer[dstIndex++] = (byte)'0'; } break; } buffer[dstIndex++] = digit; srcIndex++; numDigitsEmitted++; } } // Emit the exponent symbol buffer[dstIndex++] = exponentSymbol; if (exponent >= 0) { buffer[dstIndex++] = Utf8Constants.Plus; } else { buffer[dstIndex++] = Utf8Constants.Minus; exponent = -exponent; } Debug.Assert(exponent < Number.DECIMAL_PRECISION, "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. buffer[dstIndex++] = (byte)'0'; // The exponent for Decimal can never exceed 28 (let alone 99) buffer[dstIndex++] = (byte)((exponent / 10) + '0'); buffer[dstIndex++] = (byte)((exponent % 10) + '0'); Debug.Assert(dstIndex == numBytesNeeded); bytesWritten = numBytesNeeded; return(true); }
private static unsafe void FormatFixed(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup) { int digPos = number.scale; char* dig = number.digits; int digLength = wcslen(dig); if (digPos > 0) { if (groupDigits != null) { int groupSizeIndex = 0; // Index into the groupDigits array. int groupSizeCount = groupDigits[groupSizeIndex]; // The current total of group size. int groupSizeLen = groupDigits.Length; // The length of groupDigits array. int bufferSize = digPos; // The length of the result buffer string. int groupSeparatorLen = sGroup.Length; // The length of the group separator string. int groupSize = 0; // The current group size. // Find out the size of the string buffer for the result. if (groupSizeLen != 0) // You can pass in 0 length arrays { while (digPos > groupSizeCount) { groupSize = groupDigits[groupSizeIndex]; if (groupSize == 0) break; bufferSize += groupSeparatorLen; if (groupSizeIndex < groupSizeLen - 1) groupSizeIndex++; groupSizeCount += groupDigits[groupSizeIndex]; if (groupSizeCount < 0 || bufferSize < 0) throw new ArgumentOutOfRangeException(); // If we overflow } if (groupSizeCount == 0) // If you passed in an array with one entry as 0, groupSizeCount == 0 groupSize = 0; else groupSize = groupDigits[0]; } char* tmpBuffer = stackalloc char[bufferSize]; groupSizeIndex = 0; int digitCount = 0; int digStart; digStart = (digPos < digLength) ? digPos : digLength; char* p = tmpBuffer + bufferSize - 1; for (int i = digPos - 1; i >= 0; i--) { *(p--) = (i < digStart) ? dig[i] : '0'; if (groupSize > 0) { digitCount++; if ((digitCount == groupSize) && (i != 0)) { for (int j = groupSeparatorLen - 1; j >= 0; j--) *(p--) = sGroup[j]; if (groupSizeIndex < groupSizeLen - 1) { groupSizeIndex++; groupSize = groupDigits[groupSizeIndex]; } digitCount = 0; } } } sb.Append(tmpBuffer, bufferSize); dig += digStart; } else { int digits = Math.Min(digLength, digPos); sb.Append(dig, digits); dig += digits; if (digPos > digLength) sb.Append('0', digPos - digLength); } } else { sb.Append('0'); } if (nMaxDigits > 0) { sb.Append(sDecimal); if ((digPos < 0) && (nMaxDigits > 0)) { int zeroes = Math.Min(-digPos, nMaxDigits); sb.Append('0', zeroes); digPos += zeroes; nMaxDigits -= zeroes; } while (nMaxDigits > 0) { sb.Append((*dig != 0) ? *dig++ : '0'); nMaxDigits--; } } }
private static unsafe bool NumberToInt32(ref NumberBuffer number, ref int value) { int scale = number.scale; if ((scale > 10) || (scale < number.precision)) { return false; } char* digits = number.digits; int num2 = 0; while (--scale >= 0) { if (num2 > 0xccccccc) { return false; } num2 *= 10; if (digits[0] != '\0') { digits++; num2 += digits[0] - '0'; } } if (number.sign) { num2 = -num2; if (num2 > 0) { return false; } } else if (num2 < 0) { return false; } value = num2; return true; }
private static unsafe void FormatScientific(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar) { char* dig = number.digits; sb.Append((*dig != 0) ? *dig++ : '0'); if (nMaxDigits != 1) // For E0 we would like to suppress the decimal point sb.Append(info.NumberDecimalSeparator); while (--nMaxDigits > 0) sb.Append((*dig != 0) ? *dig++ : '0'); int e = number.digits[0] == 0 ? 0 : number.scale - 1; FormatExponent(sb, info, e, expChar, 3, true); }
private static unsafe bool NumberToInt64(ref NumberBuffer number, ref long value) { int scale = number.scale; if ((scale > 0x13) || (scale < number.precision)) { return false; } char* digits = number.digits; long num2 = 0L; while (--scale >= 0) { if (num2 > 0xcccccccccccccccL) { return false; } num2 *= 10L; if (digits[0] != '\0') { digits++; num2 += digits[0] - '0'; } } if (number.sign) { num2 = -num2; if (num2 > 0L) { return false; } } else if (num2 < 0L) { return false; } value = num2; return true; }