internal static string FormatBigInteger(BigInteger value, string format, NumberFormatInfo info)
{
int digits = 0;
char fmt = ParseFormatSpecifier(format, out digits);
if (fmt == 'x' || fmt == 'X')
return FormatBigIntegerToHexString(value, fmt, digits, info);
bool decimalFmt = (fmt == 'g' || fmt == 'G' || fmt == 'd' || fmt == 'D' || fmt == 'r' || fmt == 'R');
if (value._bits == null)
{
if (fmt == 'g' || fmt == 'G' || fmt == 'r' || fmt == 'R')
{
if (digits > 0)
format = string.Format(CultureInfo.InvariantCulture, "D{0}", digits.ToString(CultureInfo.InvariantCulture));
else
format = "D";
}
return value._sign.ToString(format, info);
}
// First convert to base 10^9.
const uint kuBase = 1000000000; // 10^9
const int kcchBase = 9;
int cuSrc = value._bits.Length;
int cuMax;
try
{
cuMax = checked(cuSrc * 10 / 9 + 2);
}
catch (OverflowException e) { throw new FormatException(SR.Format_TooLarge, e); }
uint[] rguDst = new uint[cuMax];
int cuDst = 0;
for (int iuSrc = cuSrc; --iuSrc >= 0;)
{
uint uCarry = value._bits[iuSrc];
for (int iuDst = 0; iuDst < cuDst; iuDst++)
{
Debug.Assert(rguDst[iuDst] < kuBase);
ulong uuRes = NumericsHelpers.MakeUlong(rguDst[iuDst], uCarry);
rguDst[iuDst] = (uint)(uuRes % kuBase);
uCarry = (uint)(uuRes / kuBase);
}
if (uCarry != 0)
{
rguDst[cuDst++] = uCarry % kuBase;
uCarry /= kuBase;
if (uCarry != 0)
rguDst[cuDst++] = uCarry;
}
}
int cchMax;
try
{
// Each uint contributes at most 9 digits to the decimal representation.
cchMax = checked(cuDst * kcchBase);
}
catch (OverflowException e) { throw new FormatException(SR.Format_TooLarge, e); }
if (decimalFmt)
{
if (digits > 0 && digits > cchMax)
cchMax = digits;
if (value._sign < 0)
{
try
{
// Leave an extra slot for a minus sign.
cchMax = checked(cchMax + info.NegativeSign.Length);
}
catch (OverflowException e) { throw new FormatException(SR.Format_TooLarge, e); }
}
}
int rgchBufSize;
try
{
// We'll pass the rgch buffer to native code, which is going to treat it like a string of digits, so it needs
// to be null terminated. Let's ensure that we can allocate a buffer of that size.
rgchBufSize = checked(cchMax + 1);
}
catch (OverflowException e) { throw new FormatException(SR.Format_TooLarge, e); }
char[] rgch = new char[rgchBufSize];
int ichDst = cchMax;
for (int iuDst = 0; iuDst < cuDst - 1; iuDst++)
{
uint uDig = rguDst[iuDst];
Debug.Assert(uDig < kuBase);
for (int cch = kcchBase; --cch >= 0;)
{
rgch[--ichDst] = (char)('0' + uDig % 10);
uDig /= 10;
}
}
for (uint uDig = rguDst[cuDst - 1]; uDig != 0;)
{
rgch[--ichDst] = (char)('0' + uDig % 10);
uDig /= 10;
}
if (!decimalFmt)
{
// sign = true for negative and false for 0 and positive values
bool sign = (value._sign < 0);
// The cut-off point to switch (G)eneral from (F)ixed-point to (E)xponential form
int precision = 29;
int scale = cchMax - ichDst;
return FormatProvider.FormatBigInteger(precision, scale, sign, format, info, rgch, ichDst);
}
// Format Round-trip decimal
// This format is supported for integral types only. The number is converted to a string of
// decimal digits (0-9), prefixed by a minus sign if the number is negative. The precision
// specifier indicates the minimum number of digits desired in the resulting string. If required,
// the number is padded with zeros to its left to produce the number of digits given by the
// precision specifier.
int numDigitsPrinted = cchMax - ichDst;
while (digits > 0 && digits > numDigitsPrinted)
{
// pad leading zeros
rgch[--ichDst] = '0';
digits--;
}
if (value._sign < 0)
{
string negativeSign = info.NegativeSign;
for (int i = info.NegativeSign.Length - 1; i > -1; i--)
rgch[--ichDst] = info.NegativeSign[i];
}
return new string(rgch, ichDst, cchMax - ichDst);
}
}