public bool sign; // negative sign exists public static BigNumberBuffer Create() { BigNumberBuffer number = default; number.digits = new StringBuilder(); return(number); }
private static bool NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { int scale = number.scale; int num2 = 0; value = 0; while (--scale >= 0) { value *= 10; if (number.digits[num2] != '\0') { value += number.digits[num2++] - '0'; } } while (number.digits[num2] != '\0') { if (number.digits[num2++] != '0') { return(false); } } if (number.sign) { value = -value; } return(true); }
internal static bool TryParseBigInteger(string value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { result = Zero; if (!TryValidateParseStyleInteger(style, out ArgumentException e)) { throw e; // TryParse still throws ArgumentException on invalid NumberStyles } if (value == null) { return(false); } var number = BigNumberBuffer.Create(); if (!ParseNumber(new StringProcessor(value), style, number, info)) { return(false); } if ((style & NumberStyles.AllowHexSpecifier) != 0) { if (!HexNumberToBigInteger(number, ref result)) { return(false); } } else { if (!NumberToBigInteger(number, out result)) { return(false); } } return(true); }
private static unsafe bool NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { int i = number.scale; int cur = 0; BigInteger ten = 10; value = 0; while (--i >= 0) { value *= ten; if (number.digits[cur] != '\0') { value += number.digits[cur++] - '0'; } } while (number.digits[cur] != '\0') { if (number.digits[cur++] != '0') { return(false); // Disallow non-zero trailing decimal places } } if (number.sign) { value = -value; } return(true); }
internal static bool TryParseBigInteger(ReadOnlySpan <char> value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { unsafe { result = BigInteger.Zero; ArgumentException e; if (!TryValidateParseStyleInteger(style, out e)) { throw e; // TryParse still throws ArgumentException on invalid NumberStyles } BigNumberBuffer bignumber = BigNumberBuffer.Create(); if (!FormatProvider.TryStringToBigInteger(value, style, info, bignumber.digits, out bignumber.precision, out bignumber.scale, out bignumber.sign)) { return(false); } if ((style & NumberStyles.AllowHexSpecifier) != 0) { if (!HexNumberToBigInteger(ref bignumber, ref result)) { return(false); } } else { if (!NumberToBigInteger(ref bignumber, 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); }
private unsafe static Boolean NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { Int32 i = number.scale; Int32 cur = 0; value = 0; while (--i >= 0) { value *= 10; if (number.digits[cur] != '\0') { value += (Int32)(number.digits[cur++] - '0'); } } while (number.digits[cur] != '\0') { if (number.digits[cur++] != '0') { return(false); // disallow non-zero trailing decimal places } } if (number.sign) { value = -value; } return(true); }
private static unsafe bool HexNumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { if (number.digits == null || number.digits.Length == 0) { return(false); } int len = number.digits.Length - 1; // Ignore trailing '\0' byte[] bits = new byte[(len / 2) + (len % 2)]; bool shift = false; bool isNegative = false; int bitIndex = 0; // Parse the string into a little-endian two's complement byte array // string value : O F E B 7 \0 // string index (i) : 0 1 2 3 4 5 <-- // byte[] (bitIndex): 2 1 1 0 0 <-- // for (int i = len - 1; i > -1; i--) { char c = number.digits[i]; byte b; if (c >= '0' && c <= '9') { b = (byte)(c - '0'); } else if (c >= 'A' && c <= 'F') { b = (byte)((c - 'A') + 10); } else { Debug.Assert(c >= 'a' && c <= 'f'); b = (byte)((c - 'a') + 10); } if (i == 0 && (b & 0x08) == 0x08) { isNegative = true; } if (shift) { bits[bitIndex] = (byte)(bits[bitIndex] | (b << 4)); bitIndex++; } else { bits[bitIndex] = isNegative ? (byte)(b | 0xF0) : (b); } shift = !shift; } value = new BigInteger(bits); return(true); }
private static bool HexNumberToBigInteger(BigNumberBuffer number, ref BigInteger value) { if (number.Digits == null || number.Digits.Length == 0) { return(false); } var len = number.Digits.Length; // there is no trailing '\0' var bits = new byte[(len / 2) + (len % 2)]; var shift = false; var isNegative = false; var bitIndex = 0; // parse the string into a little-endian two's complement byte array // string value : O F E B 7 \0 // string index (i) : 0 1 2 3 4 5 <-- // byte[] (bitIndex): 2 1 1 0 0 <-- // for (var i = len - 1; i > -1; i--) { var c = number.Digits[i]; byte b; if (c >= '0' && c <= '9') { b = (byte)(c - '0'); } else if (c >= 'A' && c <= 'F') { b = (byte)((c - 'A') + 10); } else { Contract.Assert(c >= 'a' && c <= 'f'); b = (byte)((c - 'a') + 10); } isNegative |= (i == 0 && (b & 0x08) == 0x08); if (shift) { bits[bitIndex] = (byte)(bits[bitIndex] | (b << 4)); bitIndex++; } else { bits[bitIndex] = isNegative ? (byte)(b | 0xF0) : (b); } shift = !shift; } value = new BigInteger(bits); return(true); }
private static bool NumberToBigInteger(BigNumberBuffer number, out BigInteger value) { var cur = 0; if (number.Scale > number.Digits.Length) { var i = number.Digits.Length; value = 0; while (--i >= 0) { value *= 10; value += (number.Digits[cur++] - '0'); } var adjust = number.Scale - number.Digits.Length; while (adjust > 9) { value *= 1000000000; adjust -= 9; } while (adjust > 0) { value *= 10; adjust--; } } else { var i = number.Scale; value = 0; while (--i >= 0) { value *= 10; value += (number.Digits[cur++] - '0'); } for (; cur < number.Digits.Length - 1; cur++) { if (number.Digits[cur++] != '0') { return(false); } } } if (number.Negative) { value = -value; } return(true); }
private static bool HexNumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { if ((number.digits == null) || (number.digits.Length == 0)) { return(false); } int num = number.digits.Length - 1; byte[] buffer = new byte[(num / 2) + (num % 2)]; bool flag = false; bool flag2 = false; int index = 0; for (int i = num - 1; i > -1; i--) { byte num4; char ch = number.digits[i]; if ((ch >= '0') && (ch <= '9')) { num4 = (byte)(ch - '0'); } else if ((ch >= 'A') && (ch <= 'F')) { num4 = (byte)((ch - 'A') + 10); } else { num4 = (byte)((ch - 'a') + 10); } if ((i == 0) && ((num4 & 8) == 8)) { flag2 = true; } if (flag) { buffer[index] = (byte)(buffer[index] | (num4 << 4)); index++; } else { buffer[index] = flag2 ? ((byte)(num4 | 240)) : num4; } flag = !flag; } value = new BigInteger(buffer); return(true); }
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); }
private static bool NumberToBigInteger(BigNumberBuffer number, out BigInteger value) { var cur = 0; if (number.Scale > number.Digits.Length) { var i = number.Digits.Length; value = 0; while (--i >= 0) { value *= 10; value += (number.Digits[cur++] - '0'); } var adjust = number.Scale - number.Digits.Length; while (adjust > 9) { value *= 1000000000; adjust -= 9; } while (adjust > 0) { value *= 10; adjust--; } } else { var i = number.Scale; value = 0; while (--i >= 0) { value *= 10; value += (number.Digits[cur++] - '0'); } for (; cur < number.Digits.Length - 1; cur++) { if (number.Digits[cur++] != '0') { return false; } } } if (number.Negative) { value = -value; } return true; }
private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { if (number.digits == null || number.digits.Length == 0) { result = default; return(false); } const int DigitsPerBlock = 8; int totalDigitCount = number.digits.Length - 1; // Ignore trailing '\0' int blockCount, partialDigitCount; blockCount = Math.DivRem(totalDigitCount, DigitsPerBlock, out int remainder); if (remainder == 0) { partialDigitCount = 0; } else { blockCount += 1; partialDigitCount = DigitsPerBlock - remainder; } bool isNegative = HexConverter.FromChar(number.digits[0]) >= 8; uint partialValue = (isNegative && partialDigitCount > 0) ? 0xFFFFFFFFu : 0; int[]? arrayFromPool = null; Span <uint> bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit) ? stackalloc uint[blockCount] : MemoryMarshal.Cast <int, uint>((arrayFromPool = ArrayPool <int> .Shared.Rent(blockCount)).AsSpan(0, blockCount)); int bitsBufferPos = blockCount - 1; try { foreach (ReadOnlyMemory <char> digitsChunkMem in number.digits.GetChunks()) { ReadOnlySpan <char> chunkDigits = digitsChunkMem.Span; for (int i = 0; i < chunkDigits.Length; i++) { char digitChar = chunkDigits[i]; if (digitChar == '\0') { break; } int hexValue = HexConverter.FromChar(digitChar); Debug.Assert(hexValue != 0xFF); partialValue = (partialValue << 4) | (uint)hexValue; partialDigitCount++; if (partialDigitCount == DigitsPerBlock) { bitsBuffer[bitsBufferPos] = partialValue; bitsBufferPos--; partialValue = 0; partialDigitCount = 0; } } } Debug.Assert(partialDigitCount == 0 && bitsBufferPos == -1); // BigInteger requires leading zero blocks to be truncated. bitsBuffer = bitsBuffer.TrimEnd(0u); int sign; uint[]? bits; if (bitsBuffer.IsEmpty) { sign = 0; bits = null; } else if (bitsBuffer.Length == 1) { sign = (int)bitsBuffer[0]; bits = null; if ((!isNegative && sign < 0) || sign == int.MinValue) { sign = isNegative ? -1 : 1; bits = new[] { (uint)sign }; } } else { sign = isNegative ? -1 : 1; bits = bitsBuffer.ToArray(); if (isNegative) { NumericsHelpers.DangerousMakeTwosComplement(bits); } } result = new BigInteger(sign, bits); return(true); } finally { if (arrayFromPool != null) { ArrayPool <int> .Shared.Return(arrayFromPool); } } }
private static bool NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { int scale = number.scale; int num2 = 0; value = 0; while (--scale >= 0) { value *= 10; if (number.digits[num2] != '\0') { value += number.digits[num2++] - '0'; } } while (number.digits[num2] != '\0') { if (number.digits[num2++] != '0') { return false; } } if (number.sign) { value = -value; } return true; }
private static bool HexNumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { if ((number.digits == null) || (number.digits.Length == 0)) { return false; } int num = number.digits.Length - 1; byte[] buffer = new byte[(num / 2) + (num % 2)]; bool flag = false; bool flag2 = false; int index = 0; for (int i = num - 1; i > -1; i--) { byte num4; char ch = number.digits[i]; if ((ch >= '0') && (ch <= '9')) { num4 = (byte) (ch - '0'); } else if ((ch >= 'A') && (ch <= 'F')) { num4 = (byte) ((ch - 'A') + 10); } else { num4 = (byte) ((ch - 'a') + 10); } if ((i == 0) && ((num4 & 8) == 8)) { flag2 = true; } if (flag) { buffer[index] = (byte) (buffer[index] | (num4 << 4)); index++; } else { buffer[index] = flag2 ? ((byte) (num4 | 240)) : num4; } flag = !flag; } value = new BigInteger(buffer); return true; }
private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { int currentBufferSize = 0; int totalDigitCount = 0; int numberScale = number.scale; const int MaxPartialDigits = 9; const uint TenPowMaxPartial = 1000000000; int[]? arrayFromPoolForResultBuffer = null; if (numberScale < 0) { result = default; return(false); } try { if (number.digits.Length <= s_naiveThreshold) { return(Naive(ref number, out result)); } else { return(DivideAndConquer(ref number, out result)); } } finally { if (arrayFromPoolForResultBuffer != null) { ArrayPool <int> .Shared.Return(arrayFromPoolForResultBuffer); } } bool Naive(ref BigNumberBuffer number, out BigInteger result) { Span <uint> stackBuffer = stackalloc uint[BigIntegerCalculator.StackAllocThreshold]; Span <uint> currentBuffer = stackBuffer; uint partialValue = 0; int partialDigitCount = 0; foreach (ReadOnlyMemory <char> digitsChunk in number.digits.GetChunks()) { if (!ProcessChunk(digitsChunk.Span, ref currentBuffer)) { result = default; return(false); } } if (partialDigitCount > 0) { MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[partialDigitCount], partialValue); } result = NumberBufferToBigInteger(currentBuffer, number.sign); return(true); bool ProcessChunk(ReadOnlySpan <char> chunkDigits, ref Span <uint> currentBuffer) { int remainingIntDigitCount = Math.Max(numberScale - totalDigitCount, 0); ReadOnlySpan <char> intDigitsSpan = chunkDigits.Slice(0, Math.Min(remainingIntDigitCount, chunkDigits.Length)); bool endReached = false; // Storing these captured variables in locals for faster access in the loop. uint _partialValue = partialValue; int _partialDigitCount = partialDigitCount; int _totalDigitCount = totalDigitCount; for (int i = 0; i < intDigitsSpan.Length; i++) { char digitChar = chunkDigits[i]; if (digitChar == '\0') { endReached = true; break; } _partialValue = _partialValue * 10 + (uint)(digitChar - '0'); _partialDigitCount++; _totalDigitCount++; // Update the buffer when enough partial digits have been accumulated. if (_partialDigitCount == MaxPartialDigits) { MultiplyAdd(ref currentBuffer, TenPowMaxPartial, _partialValue); _partialValue = 0; _partialDigitCount = 0; } } // Check for nonzero digits after the decimal point. if (!endReached) { ReadOnlySpan <char> fracDigitsSpan = chunkDigits.Slice(intDigitsSpan.Length); for (int i = 0; i < fracDigitsSpan.Length; i++) { char digitChar = fracDigitsSpan[i]; if (digitChar == '\0') { break; } if (digitChar != '0') { return(false); } } } partialValue = _partialValue; partialDigitCount = _partialDigitCount; totalDigitCount = _totalDigitCount; return(true); } } bool DivideAndConquer(ref BigNumberBuffer number, out BigInteger result) { Span <uint> currentBuffer; int[]? arrayFromPoolForMultiplier = null; try { totalDigitCount = Math.Min(number.digits.Length - 1, numberScale); int bufferSize = (totalDigitCount + MaxPartialDigits - 1) / MaxPartialDigits; Span <uint> buffer = new uint[bufferSize]; arrayFromPoolForResultBuffer = ArrayPool <int> .Shared.Rent(bufferSize); Span <uint> newBuffer = MemoryMarshal.Cast <int, uint>(arrayFromPoolForResultBuffer).Slice(0, bufferSize); newBuffer.Clear(); // Separate every MaxPartialDigits digits and store them in the buffer. // Buffers are treated as little-endian. That means, the array { 234567890, 1 } // represents the number 1234567890. int bufferIndex = bufferSize - 1; uint currentBlock = 0; int shiftUntil = (totalDigitCount - 1) % MaxPartialDigits; int remainingIntDigitCount = totalDigitCount; foreach (ReadOnlyMemory <char> digitsChunk in number.digits.GetChunks()) { ReadOnlySpan <char> digitsChunkSpan = digitsChunk.Span; ReadOnlySpan <char> intDigitsSpan = digitsChunkSpan.Slice(0, Math.Min(remainingIntDigitCount, digitsChunkSpan.Length)); for (int i = 0; i < intDigitsSpan.Length; i++) { char digitChar = intDigitsSpan[i]; Debug.Assert(char.IsDigit(digitChar)); currentBlock *= 10; currentBlock += unchecked ((uint)(digitChar - '0')); if (shiftUntil == 0) { buffer[bufferIndex] = currentBlock; currentBlock = 0; bufferIndex--; shiftUntil = MaxPartialDigits; } shiftUntil--; } remainingIntDigitCount -= intDigitsSpan.Length; Debug.Assert(0 <= remainingIntDigitCount); ReadOnlySpan <char> fracDigitsSpan = digitsChunkSpan.Slice(intDigitsSpan.Length); for (int i = 0; i < fracDigitsSpan.Length; i++) { char digitChar = fracDigitsSpan[i]; if (digitChar == '\0') { break; } if (digitChar != '0') { result = default; return(false); } } } Debug.Assert(currentBlock == 0); Debug.Assert(bufferIndex == -1); int blockSize = 1; arrayFromPoolForMultiplier = ArrayPool <int> .Shared.Rent(blockSize); Span <uint> multiplier = MemoryMarshal.Cast <int, uint>(arrayFromPoolForMultiplier).Slice(0, blockSize); multiplier[0] = TenPowMaxPartial; // This loop is executed ceil(log_2(bufferSize)) times. while (true) { // merge each block pairs. // When buffer represents: // | A | B | C | D | // Make newBuffer like: // | A + B * multiplier | C + D * multiplier | for (int i = 0; i < bufferSize; i += blockSize * 2) { Span <uint> curBuffer = buffer.Slice(i); Span <uint> curNewBuffer = newBuffer.Slice(i); int len = Math.Min(bufferSize - i, blockSize * 2); int lowerLen = Math.Min(len, blockSize); int upperLen = len - lowerLen; if (upperLen != 0) { Debug.Assert(blockSize == lowerLen); Debug.Assert(blockSize == multiplier.Length); Debug.Assert(multiplier.Length == lowerLen); BigIntegerCalculator.Multiply(multiplier, curBuffer.Slice(blockSize, upperLen), curNewBuffer.Slice(0, len)); } long carry = 0; int j = 0; for (; j < lowerLen; j++) { long digit = (curBuffer[j] + carry) + curNewBuffer[j]; curNewBuffer[j] = unchecked ((uint)digit); carry = digit >> 32; } if (carry != 0) { while (true) { curNewBuffer[j]++; if (curNewBuffer[j] != 0) { break; } j++; } } } Span <uint> tmp = buffer; buffer = newBuffer; newBuffer = tmp; blockSize *= 2; if (bufferSize <= blockSize) { break; } newBuffer.Clear(); int[]? arrayToReturn = arrayFromPoolForMultiplier; arrayFromPoolForMultiplier = ArrayPool <int> .Shared.Rent(blockSize); Span <uint> newMultiplier = MemoryMarshal.Cast <int, uint>(arrayFromPoolForMultiplier).Slice(0, blockSize); newMultiplier.Clear(); BigIntegerCalculator.Square(multiplier, newMultiplier); multiplier = newMultiplier; if (arrayToReturn is not null) { ArrayPool <int> .Shared.Return(arrayToReturn); } } // shrink buffer to the currently used portion. // First, calculate the rough size of the buffer from the ratio that the number // of digits follows. Then, shrink the size until there is no more space left. // The Ratio is calculated as: log_{2^32}(10^9) const double digitRatio = 0.934292276687070661; currentBufferSize = Math.Min((int)(bufferSize * digitRatio) + 1, bufferSize); Debug.Assert(buffer.Length == currentBufferSize || buffer[currentBufferSize] == 0); while (0 < currentBufferSize && buffer[currentBufferSize - 1] == 0) { currentBufferSize--; } currentBuffer = buffer.Slice(0, currentBufferSize); result = NumberBufferToBigInteger(currentBuffer, number.sign); } finally { if (arrayFromPoolForMultiplier != null) { ArrayPool <int> .Shared.Return(arrayFromPoolForMultiplier); } } return(true); } BigInteger NumberBufferToBigInteger(Span <uint> currentBuffer, bool signa) { int trailingZeroCount = numberScale - totalDigitCount; while (trailingZeroCount >= MaxPartialDigits) { MultiplyAdd(ref currentBuffer, TenPowMaxPartial, 0); trailingZeroCount -= MaxPartialDigits; } if (trailingZeroCount > 0) { MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[trailingZeroCount], 0); } int sign; uint[]? bits; if (currentBufferSize == 0) { sign = 0; bits = null; } else if (currentBufferSize == 1 && currentBuffer[0] <= int.MaxValue) { sign = (int)(signa ? -currentBuffer[0] : currentBuffer[0]); bits = null; } else { sign = signa ? -1 : 1; bits = currentBuffer.Slice(0, currentBufferSize).ToArray(); } return(new BigInteger(sign, bits)); } // This function should only be used for result buffer. void MultiplyAdd(ref Span <uint> currentBuffer, uint multiplier, uint addValue) { Span <uint> curBits = currentBuffer.Slice(0, currentBufferSize); uint carry = addValue; for (int i = 0; i < curBits.Length; i++) { ulong p = (ulong)multiplier * curBits[i] + carry; curBits[i] = (uint)p; carry = (uint)(p >> 32); } if (carry == 0) { return; } if (currentBufferSize == currentBuffer.Length) { int[]? arrayToReturn = arrayFromPoolForResultBuffer; arrayFromPoolForResultBuffer = ArrayPool <int> .Shared.Rent(checked (currentBufferSize * 2)); Span <uint> newBuffer = MemoryMarshal.Cast <int, uint>(arrayFromPoolForResultBuffer); currentBuffer.CopyTo(newBuffer); currentBuffer = newBuffer; if (arrayToReturn != null) { ArrayPool <int> .Shared.Return(arrayToReturn); } } currentBuffer[currentBufferSize] = carry; currentBufferSize++; } }
private unsafe static bool NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { int i = number.scale; int cur = 0; BigInteger ten = 10; value = 0; while (--i >= 0) { value *= ten; if (number.digits[cur] != '\0') { value += number.digits[cur++] - '0'; } } while (number.digits[cur] != '\0') { if (number.digits[cur++] != '0') return false; // Disallow non-zero trailing decimal places } if (number.sign) { value = -value; } return true; }
private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { Span <uint> stackBuffer = stackalloc uint[BigInteger.StackallocUInt32Limit]; Span <uint> currentBuffer = stackBuffer; int currentBufferSize = 0; int[]? arrayFromPool = null; uint partialValue = 0; int partialDigitCount = 0; int totalDigitCount = 0; int numberScale = number.scale; const int MaxPartialDigits = 9; const uint TenPowMaxPartial = 1000000000; try { foreach (ReadOnlyMemory <char> digitsChunk in number.digits.GetChunks()) { if (!ProcessChunk(digitsChunk.Span, ref currentBuffer)) { result = default; return(false); } } if (partialDigitCount > 0) { MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[partialDigitCount], partialValue); } int trailingZeroCount = numberScale - totalDigitCount; while (trailingZeroCount >= MaxPartialDigits) { MultiplyAdd(ref currentBuffer, TenPowMaxPartial, 0); trailingZeroCount -= MaxPartialDigits; } if (trailingZeroCount > 0) { MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[trailingZeroCount], 0); } int sign; uint[]? bits; if (currentBufferSize == 0) { sign = 0; bits = null; } else if (currentBufferSize == 1 && currentBuffer[0] <= int.MaxValue) { sign = (int)(number.sign ? -currentBuffer[0] : currentBuffer[0]); bits = null; } else { sign = number.sign ? -1 : 1; bits = currentBuffer.Slice(0, currentBufferSize).ToArray(); } result = new BigInteger(sign, bits); return(true); } finally { if (arrayFromPool != null) { ArrayPool <int> .Shared.Return(arrayFromPool); } } bool ProcessChunk(ReadOnlySpan <char> chunkDigits, ref Span <uint> currentBuffer) { int remainingIntDigitCount = Math.Max(numberScale - totalDigitCount, 0); ReadOnlySpan <char> intDigitsSpan = chunkDigits.Slice(0, Math.Min(remainingIntDigitCount, chunkDigits.Length)); bool endReached = false; // Storing these captured variables in locals for faster access in the loop. uint _partialValue = partialValue; int _partialDigitCount = partialDigitCount; int _totalDigitCount = totalDigitCount; for (int i = 0; i < intDigitsSpan.Length; i++) { char digitChar = chunkDigits[i]; if (digitChar == '\0') { endReached = true; break; } _partialValue = _partialValue * 10 + (uint)(digitChar - '0'); _partialDigitCount++; _totalDigitCount++; // Update the buffer when enough partial digits have been accumulated. if (_partialDigitCount == MaxPartialDigits) { MultiplyAdd(ref currentBuffer, TenPowMaxPartial, _partialValue); _partialValue = 0; _partialDigitCount = 0; } } // Check for nonzero digits after the decimal point. if (!endReached) { ReadOnlySpan <char> fracDigitsSpan = chunkDigits.Slice(intDigitsSpan.Length); for (int i = 0; i < fracDigitsSpan.Length; i++) { char digitChar = fracDigitsSpan[i]; if (digitChar == '\0') { break; } if (digitChar != '0') { return(false); } } } partialValue = _partialValue; partialDigitCount = _partialDigitCount; totalDigitCount = _totalDigitCount; return(true); } void MultiplyAdd(ref Span <uint> currentBuffer, uint multiplier, uint addValue) { Span <uint> curBits = currentBuffer.Slice(0, currentBufferSize); uint carry = addValue; for (int i = 0; i < curBits.Length; i++) { ulong p = (ulong)multiplier * curBits[i] + carry; curBits[i] = (uint)p; carry = (uint)(p >> 32); } if (carry == 0) { return; } if (currentBufferSize == currentBuffer.Length) { int[]? arrayToReturn = arrayFromPool; arrayFromPool = ArrayPool <int> .Shared.Rent(checked (currentBufferSize * 2)); Span <uint> newBuffer = MemoryMarshal.Cast <int, uint>(arrayFromPool); currentBuffer.CopyTo(newBuffer); currentBuffer = newBuffer; if (arrayToReturn != null) { ArrayPool <int> .Shared.Return(arrayToReturn); } } currentBuffer[currentBufferSize] = carry; currentBufferSize++; } }
public bool sign; // negative sign exists public static BigNumberBuffer Create() { BigNumberBuffer number = new BigNumberBuffer(); number.digits = new StringBuilder(); return number; }
private unsafe static Boolean HexNumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { if (number.digits == null || number.digits.Length == 0) return false; int len = number.digits.Length - 1; // ignore trailing '\0' byte[] bits = new byte[(len / 2) + (len % 2)]; bool shift = false; bool isNegative = false; int bitIndex = 0; // parse the string into a little-endian two's complement byte array // string value : O F E B 7 \0 // string index (i) : 0 1 2 3 4 5 <-- // byte[] (bitIndex): 2 1 1 0 0 <-- // for (int i = len-1; i > -1; i--) { char c = number.digits[i]; byte b; if (c >= '0' && c <= '9') { b = (byte)(c - '0'); } else if (c >= 'A' && c <= 'F') { b = (byte)((c - 'A') + 10); } else { Contract.Assert(c >= 'a' && c <= 'f'); b = (byte)((c - 'a') + 10); } if (i == 0 && (b & 0x08) == 0x08) isNegative = true; if (shift) { bits[bitIndex] = (byte)(bits[bitIndex] | (b << 4)); bitIndex++; } else { bits[bitIndex] = isNegative ? (byte)(b | 0xF0) : (b); } shift = !shift; } value = new BigInteger(bits); return true; }
private unsafe static Boolean NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) { Int32 i = number.scale; Int32 cur = 0; value = 0; while (--i >= 0) { value *= 10; if (number.digits[cur] != '\0') { value += (Int32)(number.digits[cur++] - '0'); } } while (number.digits[cur] != '\0') { if (number.digits[cur++] != '0') return false; // disallow non-zero trailing decimal places } if (number.sign) { value = -value; } return true; }
internal static bool ParseNumber(StringProcessor reader, NumberStyles options, BigNumberBuffer number, NumberFormatInfo info) { if ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None) { var allowLeadingWhite = (options & NumberStyles.AllowLeadingWhite) != NumberStyles.None; var allowTrailingWhite = (options & NumberStyles.AllowTrailingWhite) != NumberStyles.None; /* * // Assume validated * if ( * (options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None || (options & NumberStyles.AllowLeadingSign) != NumberStyles.None || (options & NumberStyles.AllowParentheses) != NumberStyles.None || (options & NumberStyles.AllowThousands) != NumberStyles.None || (options & NumberStyles.AllowExponent) != NumberStyles.None || (options & NumberStyles.AllowTrailingSign) != NumberStyles.None || ) ||{ || return false; ||}*/ number.Negative = false; if (allowLeadingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } while (true) { var input = reader.ReadWhile ( new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F' } ); if (input.Length == 0) { break; } number.Scale += input.Length; number.Digits.Append(input.ToUpperInvariant()); } if (allowTrailingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } return(reader.EndOfString); } else { var allowCurrencySymbol = (options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None; var allowLeadingWhite = (options & NumberStyles.AllowLeadingWhite) != NumberStyles.None; var allowLeadingSign = (options & NumberStyles.AllowLeadingSign) != NumberStyles.None; var allowParentheses = (options & NumberStyles.AllowParentheses) != NumberStyles.None; var allowThousands = (options & NumberStyles.AllowThousands) != NumberStyles.None; var allowExponent = (options & NumberStyles.AllowExponent) != NumberStyles.None; var allowTrailingWhite = (options & NumberStyles.AllowTrailingWhite) != NumberStyles.None; var allowTrailingSign = (options & NumberStyles.AllowTrailingSign) != NumberStyles.None; var allowDecimalPoint = (options & NumberStyles.AllowDecimalPoint) != NumberStyles.None; var isCurrency = false; number.Negative = false; var waitingParentheses = false; var positive = false; // [ws][$][sign][digits,]digits[E[sign]exponential_digits][ws] if (allowLeadingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } // Percent intentionally not supported // After testing with .NET the patterns are ignored... all patterns are welcome var currencySymbol = info.CurrencySymbol; // [$][sign][digits,]digits[E[sign]exponential_digits][ws] if (allowCurrencySymbol && reader.Read(currencySymbol)) { isCurrency = true; reader.SkipWhile(CharHelper.IsClassicWhitespace); } var positiveSign = info.PositiveSign; var negativeSign = info.NegativeSign; // [sign][digits,]digits[E[sign]exponential_digits][ws if (allowLeadingSign) { number.Negative |= reader.Read(negativeSign); if (!number.Negative) { positive |= reader.Read(positiveSign); } } if (!number.Negative && allowParentheses && reader.Read('(')) { // Testing on .NET show that $(n) is allowed, even tho there is no CurrencyNegativePattern for it number.Negative = true; waitingParentheses = true; } // --- if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) // If the currency symbol is after the negative sign { isCurrency = true; reader.SkipWhile(CharHelper.IsClassicWhitespace); } // [digits,]digits[E[sign]exponential_digits][ws] var failure = true; var digits = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; var decimalFound = false; while (true) { var input = reader.ReadWhile(digits); if (input.Length == 0) { if (allowDecimalPoint && !decimalFound) { if (reader.Read(info.CurrencyDecimalSeparator)) { decimalFound = true; continue; } if (reader.Read(info.NumberDecimalSeparator)) { if (isCurrency) { return(false); } decimalFound = true; continue; } } break; } failure = false; if (!decimalFound) { number.Scale += input.Length; } number.Digits.Append(input); if (!allowThousands) { continue; } var currencyGroupSeparator = info.CurrencyGroupSeparator.ToCharArray(); // Testing on .NET show that combining currency and number group separators is allowed // But not if the currency symbol has already appeared reader.SkipWhile(currencyGroupSeparator); if (isCurrency) { continue; } var numberGroupSeparator = info.NumberGroupSeparator.ToCharArray(); reader.SkipWhile(numberGroupSeparator); } if (failure) { return(false); } // [E[sign]exponential_digits][ws] if (allowExponent && (reader.Read('E') || reader.Read('e'))) { // [sign]exponential_digits // Testing on .NET show that no pattern is used here, also no parentheses nor group separators supported // The exponent can be big - but anything beyond 9999 is ignored var exponentNegative = reader.Read(negativeSign); if (!exponentNegative) { reader.Read(positiveSign); } var input = reader.ReadWhile(digits); var exponentMagnitude = int.Parse(input, CultureInfo.InvariantCulture); number.Scale += (exponentNegative ? -1 : 1) * (input.Length > 4 ? 9999 : exponentMagnitude); if (number.Scale < 0) { return(false); } } // --- if (allowTrailingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) { isCurrency = true; } // --- if (!number.Negative && !positive && allowTrailingSign) { number.Negative |= reader.Read(negativeSign); if (!number.Negative) { reader.Read(positiveSign); } } if (waitingParentheses && !reader.Read(')')) { return(false); } // --- if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) // If the currency symbol is after the negative sign { /*isCurrency = true; // For completeness sake*/ } // [ws] if (allowTrailingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } return(reader.EndOfString); } }
internal static bool ParseNumber(StringProcessor reader, NumberStyles options, BigNumberBuffer number, NumberFormatInfo info) { // Percent intentionally not supported // After testig with .NET the patterns are ignored... all patterns are welcome var currencySymbol = info.CurrencySymbol; var numberGroupSeparator = info.NumberGroupSeparator; var currencyGroupSeparator = info.CurrencyGroupSeparator; var positiveSign = info.PositiveSign; var negativeSign = info.NegativeSign; if ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None) { var allowLeadingWhite = (options & NumberStyles.AllowLeadingWhite) != NumberStyles.None; var allowTrailingWhite = (options & NumberStyles.AllowTrailingWhite) != NumberStyles.None; /* // Assume validated if ( (options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None || (options & NumberStyles.AllowLeadingSign) != NumberStyles.None || (options & NumberStyles.AllowParentheses) != NumberStyles.None || (options & NumberStyles.AllowThousands) != NumberStyles.None || (options & NumberStyles.AllowExponent) != NumberStyles.None || (options & NumberStyles.AllowTrailingSign) != NumberStyles.None ) { return false; }*/ number.Negative = false; if (allowLeadingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } while (true) { var input = reader.ReadWhile(new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F' }); if (input == string.Empty) { break; } number.Scale += input.Length; number.Digits.Append(input.ToUpperInvariant()); } if (allowTrailingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } return reader.EndOfString; } else { var allowCurrencySymbol = (options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None; var allowLeadingWhite = (options & NumberStyles.AllowLeadingWhite) != NumberStyles.None; var allowLeadingSign = (options & NumberStyles.AllowLeadingSign) != NumberStyles.None; var allowParentheses = (options & NumberStyles.AllowParentheses) != NumberStyles.None; var allowThousands = (options & NumberStyles.AllowThousands) != NumberStyles.None; var allowExponent = (options & NumberStyles.AllowExponent) != NumberStyles.None; var allowTrailingWhite = (options & NumberStyles.AllowTrailingWhite) != NumberStyles.None; var allowTrailingSign = (options & NumberStyles.AllowTrailingSign) != NumberStyles.None; var allowDecimalPoint = (options & NumberStyles.AllowDecimalPoint) != NumberStyles.None; var isCurrency = false; number.Negative = false; var waitingParentheses = false; var positive = false; // [ws][$][sign][digits,]digits[E[sign]exponential_digits][ws] if (allowLeadingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } // [$][sign][digits,]digits[E[sign]exponential_digits][ws] if (allowCurrencySymbol && reader.Read(currencySymbol)) { isCurrency = true; reader.SkipWhile(CharHelper.IsClassicWhitespace); } // [sign][digits,]digits[E[sign]exponential_digits][ws if (allowLeadingSign) { if (reader.Read(negativeSign)) { number.Negative = true; } if (reader.Read(positiveSign)) { positive = true; } } if (!number.Negative && allowParentheses && reader.Read('(')) { // Testing on .NET show that $(n) is allowed, even tho there is no CurrencyNegativePattern for it number.Negative = true; waitingParentheses = true; } // --- if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) // If the currency symbol is after the negative sign { isCurrency = true; reader.SkipWhile(CharHelper.IsClassicWhitespace); } // [digits,]digits[E[sign]exponential_digits][ws] var failure = true; var digits = new[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; var decimalFound = false; while (true) { var input = reader.ReadWhile(digits); if (input == string.Empty) { if (allowDecimalPoint && !decimalFound) { if (reader.Read(info.CurrencyDecimalSeparator)) { decimalFound = true; continue; } if (reader.Read(info.NumberDecimalSeparator)) { if (isCurrency) { return false; } decimalFound = true; continue; } } break; } failure = false; if (!decimalFound) { number.Scale += input.Length; } number.Digits.Append(input); if (allowThousands) { // Testing on .NET show that combining currency and number group separators is allowed // But not if the currency symbol has already appeared reader.SkipWhile(currencyGroupSeparator); if (!isCurrency) { reader.SkipWhile(numberGroupSeparator); } } } if (failure) { return false; } // [E[sign]exponential_digits][ws] if (allowExponent && (reader.Read('E') || reader.Read('e'))) { // [sign]exponential_digits // Testing on .NET show that no pattern is used here, also no parentheses nor group separators supported // The exponent can be big - but anything beyond 9999 is ignored var exponentNegative = reader.Read(negativeSign); if (!exponentNegative) { reader.Read(positiveSign); } var input = reader.ReadWhile(digits); var exponentMagnitude = int.Parse(input, CultureInfo.InvariantCulture); number.Scale += (exponentNegative ? -1 : 1) * (input.Length > 4 ? 9999 : exponentMagnitude); if (number.Scale < 0) { return false; } } // --- if (allowTrailingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) { isCurrency = true; } // --- if (!number.Negative && !positive && allowTrailingSign) { if (reader.Read(negativeSign)) { number.Negative = true; } if (reader.Read(positiveSign)) { positive = true; // For completness sake } } if (waitingParentheses && !reader.Read(')')) { return false; } // --- if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) // If the currency symbol is after the negative sign { isCurrency = true; // For completeness sake } // [ws] if (allowTrailingWhite) { reader.SkipWhile(CharHelper.IsClassicWhitespace); } return reader.EndOfString; } }