|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#undef COMPARE_BCL
using System;
using Microsoft.ML.Runtime;
namespace Microsoft.ML.Internal.Utilities
{
[BestFriend]
internal static class DoubleParser
{
[BestFriend]
[Flags]
internal enum OptionFlags : uint
{
Default = 0x00,
// If this flag is set, then a "," will be used as Decimal Marker
// (i.e., the punctuation mark that separates the integer part of
// a number and its decimal part). If this isn't set, then
// default behavior is to use "." as decimal marker.
UseCommaAsDecimalMarker = 0x01,
// If this flag is set, then empty spans (or those with only white-space)
// will be parsed as NaN. If it isn't set, then default behavior
// is to return them as 0.
EmptyAsNaN = 0x02,
}
private const ulong TopBit = 0x8000000000000000UL;
private const ulong TopTwoBits = 0xC000000000000000UL;
private const ulong TopThreeBits = 0xE000000000000000UL;
private const char InfinitySymbol = '\u221E';
// REVIEW: casting ulong to Double doesn't always do the right thing, for example
// with 0x84595161401484A0UL. Hence the gymnastics several places in this code. Note that
// long to Double does work. The work around is:
// if ((long)uu >= 0)
// dbl = (Double)(long)uu;
// else
// {
// dbl = (Double)(long)((uu >> 1) | (uu & 1));
// dbl += dbl;
// }
// Note that proper rounding to Double uses the "round to even" rule when there is a "tie".
// That is, when the source is exactly half way between the two closest destination values,
// the value with zero lowest bit is preferred. Oring (uu & 1) above ensures that we don't
// let a non-tie become a tie. That is, if the low bit (that we're dropping) is 1, we capture
// the fact that there are non-zero bits beyond the critical rounding bit, so the rounding
// code knows that we don't have a tie.
// When COMPARE_BCL is defined, we assert that we get the same result as the BCL. Unfortunately,
// the BCL currently gets values wrong. For example, it maps 1.48e-323 to 0x02 instead of 0x03.
#if COMPARE_BCL
// Whether we've reported a failure to match Double.TryParse. Only report the first failure.
private volatile static bool _failed;
#endif
/// <summary>
/// Result codes from Parse methods. Note that the order matters.
/// </summary>
public enum Result
{
/// <summary>
/// No issues.
/// </summary>
Good = 0,
/// <summary>
/// Empty or only whitespace
/// </summary>
Empty = 1,
/// <summary>
/// Extra non-whitespace characters after successful parse
/// </summary>
Extra = 2,
/// <summary>
/// Parsing error
/// </summary>
Error = 3
}
/// <summary>
/// This produces zero for an empty string, or NaN depending on the <see cref="DoubleParser.OptionFlags.EmptyAsNaN"/> used.
/// </summary>
public static bool TryParse(ReadOnlySpan<char> span, out Single value, OptionFlags flags = OptionFlags.Default)
{
var res = Parse(span, out value, flags);
Contracts.Assert(res != Result.Empty || ((flags & OptionFlags.EmptyAsNaN) == 0 && value == 0) || Single.IsNaN(value));
return res <= Result.Empty;
}
/// <summary>
/// This produces zero for an empty string, or NaN depending on the <see cref="DoubleParser.OptionFlags.EmptyAsNaN"/> used.
/// </summary>
public static bool TryParse(ReadOnlySpan<char> span, out Double value, OptionFlags flags = OptionFlags.Default)
{
var res = Parse(span, out value, flags);
Contracts.Assert(res != Result.Empty || ((flags & OptionFlags.EmptyAsNaN) == 0 && value == 0) || Double.IsNaN(value));
return res <= Result.Empty;
}
public static Result Parse(ReadOnlySpan<char> span, out Single value, OptionFlags flags = OptionFlags.Default)
{
int ich = 0;
for (; ; ich++)
{
if (ich >= span.Length)
{
if ((flags & OptionFlags.EmptyAsNaN) == 0)
value = 0;
else
value = Single.NaN;
return Result.Empty;
}
if (!char.IsWhiteSpace(span[ich]))
break;
}
// Handle the common case of a single digit or ?
if (span.Length - ich == 1)
{
char ch = span[ich];
if (ch >= '0' && ch <= '9')
{
value = ch - '0';
return Result.Good;
}
if (ch == '?')
{
value = Single.NaN;
return Result.Good;
}
}
int ichEnd;
if (!DoubleParser.TryParse(span.Slice(ich, span.Length - ich), out value, out ichEnd, flags))
{
value = default(Single);
return Result.Error;
}
// Make sure everything was consumed.
while (ichEnd < span.Length)
{
if (!char.IsWhiteSpace(span[ichEnd]))
return Result.Extra;
ichEnd++;
}
return Result.Good;
}
public static Result Parse(ReadOnlySpan<char> span, out Double value, OptionFlags flags = OptionFlags.Default)
{
int ich = 0;
for (; ; ich++)
{
if (ich >= span.Length)
{
if ((flags & OptionFlags.EmptyAsNaN) == 0)
value = 0;
else
value = Double.NaN;
return Result.Empty;
}
if (!char.IsWhiteSpace(span[ich]))
break;
}
// Handle the common case of a single digit or ?
if (span.Length - ich == 1)
{
char ch = span[ich];
if (ch >= '0' && ch <= '9')
{
value = ch - '0';
return Result.Good;
}
if (ch == '?')
{
value = Double.NaN;
return Result.Good;
}
}
int ichEnd;
if (!DoubleParser.TryParse(span.Slice(ich, span.Length - ich), out value, out ichEnd, flags))
{
value = default(Double);
return Result.Error;
}
// Make sure everything was consumed.
while (ichEnd < span.Length)
{
if (!char.IsWhiteSpace(span[ichEnd]))
return Result.Extra;
ichEnd++;
}
return Result.Good;
}
public static bool TryParse(ReadOnlySpan<char> span, out Single value, out int ichEnd, OptionFlags flags = OptionFlags.Default)
{
bool neg = false;
ulong num = 0;
long exp = 0;
ichEnd = 0;
if (!TryParseCore(span, ref ichEnd, ref neg, ref num, ref exp, flags))
return TryParseSpecial(span, ref ichEnd, out value);
if (num == 0)
{
value = 0;
goto LDone;
}
// The Single version simply looks up the power of 10 in a table (as a Double), casts num
// to Double, multiples, then casts to Single.
Double res;
if (exp >= 0)
{
if (exp == 0)
res = 1;
else if (exp > _mpe10Dbl.Length)
{
value = Single.PositiveInfinity;
goto LDone;
}
else
res = _mpe10Dbl[(int)exp - 1];
}
else
{
if (-exp > _mpne10Dbl.Length)
{
value = 0;
goto LDone;
}
res = _mpne10Dbl[-(int)exp - 1];
}
// REVIEW: casting ulong to Double doesn't always get the answer correct.
// Casting a non-negative long does work, though, so we jump through hoops to make
// sure the top bit is clear and cast to long before casting to Double.
Double tmp;
if ((long)num >= 0)
tmp = (Double)(long)num;
else
{
tmp = (Double)(long)((num >> 1) | (num & 1));
tmp += tmp;
}
res *= tmp;
value = (Single)res;
LDone:
if (neg)
value = -value;
#if COMPARE_BCL
if (!_failed)
{
string str = span.ToString();
Single x;
if (!Single.TryParse(str, out x))
{
// Single.TryParse doesn't gracefully handle overflow to infinity.
if (!Single.IsPositiveInfinity(value) && !Single.IsNegativeInfinity(value))
{
_failed = true;
Contracts.Assert(false, string.Format("Single.TryParse failed on: {0}", str));
}
}
else if (FloatUtils.GetBits(x) != FloatUtils.GetBits(value))
{
// Double.TryParse gets negative zero wrong!
if (FloatUtils.GetBits(x) != 0 || FloatUtils.GetBits(value) != 0x80000000U || !neg)
{
_failed = true;
Contracts.Assert(false, string.Format("FloatParser disagrees with Single.TryParse on: {0} ({1} vs {2})", str, FloatUtils.GetBits(x), FloatUtils.GetBits(value)));
}
}
}
#endif
return true;
}
public static bool TryParse(ReadOnlySpan<char> span, out Double value, out int ichEnd, OptionFlags flags = OptionFlags.Default)
{
bool neg = false;
ulong num = 0;
long exp = 0;
ichEnd = 0;
if (!TryParseCore(span, ref ichEnd, ref neg, ref num, ref exp, flags))
return TryParseSpecial(span, ref ichEnd, out value);
if (num == 0)
{
value = 0;
goto LDone;
}
ulong mul;
int e2;
if (exp >= 0)
{
if (exp == 0)
{
// REVIEW: casting ulong to Double doesn't always get the answer correct.
// Casting a non-negative long does work, though, so we jump through hoops to make
// sure the top bit is clear and cast to long before casting to Double.
if ((long)num >= 0)
value = (Double)(long)num;
else
{
value = (Double)(long)((num >> 1) | (num & 1));
value += value;
}
goto LDone;
}
if (exp <= 22 && num < (1UL << 53))
{
// Can just use Double division, since both 10^|exp| and num can be exactly represented in Double.
// REVIEW: there are potential other "easy" cases, like when num isn't less than 2^54, but
// it ends with enough zeros that it can be made so by shifting.
Contracts.Assert((long)(Double)(long)num == (long)num);
value = (Double)(long)num * _mpe10Dbl[exp - 1];
goto LDone;
}
if (exp > _mpe10Man.Length)
{
value = Double.PositiveInfinity;
goto LDone;
}
int index = (int)exp - 1;
mul = _mpe10Man[index];
e2 = _mpe10e2[index];
}
else
{
if (-exp <= 22 && num < (1UL << 53))
{
// Can just use Double division, since both 10^|exp| and num can be exactly represented in Double.
// REVIEW: there are potential other "easy" cases, like when num isn't less than 2^54, but
// it ends with enough zeros that it can be made so by shifting.
Contracts.Assert((long)(Double)(long)num == (long)num);
value = (Double)(long)num / _mpe10Dbl[-exp - 1];
goto LDone;
}
if (-exp > _mpne10Man.Length)
{
value = 0;
goto LDone;
}
int index = -(int)exp - 1;
mul = _mpne10Man[index];
e2 = -_mpne10ne2[index];
}
// Normalize the mantissa and initialize the base-2 (biased) exponent.
// Ensure that the high bit is set.
if ((num & 0xFFFFFFFF00000000UL) == 0) { num <<= 32; e2 -= 32; }
if ((num & 0xFFFF000000000000UL) == 0) { num <<= 16; e2 -= 16; }
if ((num & 0xFF00000000000000UL) == 0) { num <<= 8; e2 -= 8; }
if ((num & 0xF000000000000000UL) == 0) { num <<= 4; e2 -= 4; }
if ((num & 0xC000000000000000UL) == 0) { num <<= 2; e2 -= 2; }
// Don't force the top bit to be non-zero, since we'll just have to clear it later....
// if ((num & 0x8000000000000000UL) == 0) { num <<= 1; e2 -= 1; }
// The high bit of mul is 1, and at least one of the top two bits of num is non-zero.
// Taking the high 64 bits of the 128 bit result should give us enough bits to get the
// right answer most of the time. Note, that it's not guaranteed that we always get the
// right answer. Guaranteeing that takes much more work.... See the paper by David Gay at
// https://www.ampl.com/REFS/rounding.pdf.
Contracts.Assert((num & TopTwoBits) != 0);
Contracts.Assert((mul & TopBit) != 0);
// Multiply mul into num, keeping the high ulong.
// REVIEW: Can this be made faster? It would be nice to use the _umul128 intrinsic.
ulong hi1 = mul >> 32;
ulong lo1 = mul & 0xFFFFFFFFUL;
ulong hi2 = num >> 32;
ulong lo2 = num & 0xFFFFFFFFUL;
num = hi1 * hi2;
hi1 *= lo2;
hi2 *= lo1;
num += hi1 >> 32;
num += hi2 >> 32;
hi1 <<= 32;
hi2 <<= 32;
if ((hi1 += hi2) < hi2)
num++;
lo1 *= lo2;
if ((lo1 += hi1) < hi1)
num++;
// Cast to long first since ulong => Double is broken.
Contracts.Assert((num & TopThreeBits) != 0);
if ((long)num >= 0)
{
// Capture a non-zero lo to avoid an artificial tie.
if (lo1 != 0)
num |= 1;
value = (Double)(long)num;
}
else
{
if ((lo1 | (num & 1)) != 0)
num |= 2;
value = (Double)(long)(num >> 1);
e2++;
}
// Adjust the base-2 exponent.
e2 += 0x3FF;
if (e2 >= 0x7FF)
{
Contracts.Assert(exp > 0);
value = Double.PositiveInfinity;
goto LDone;
}
if (e2 <= 0)
{
// Break the exponent adjustment into two operations.
Contracts.Assert(exp < 0);
mul = 1UL << 52;
unsafe { value *= *(Double*)&mul; }
e2 += 0x3FF - 1;
Contracts.Assert(e2 > 0);
}
// Multiply by the exponent adjustment.
Contracts.Assert(0 < e2 && e2 < 0x7FF);
mul = (ulong)e2 << 52;
unsafe { value *= *(Double*)&mul; }
LDone:
if (neg)
value = -value;
#if COMPARE_BCL
string str = span.ToString();
Double x;
if (!Double.TryParse(str, out x))
{
// Double.TryParse doesn't gracefully handle overflow to infinity.
if (!Double.IsPositiveInfinity(value) && !Double.IsNegativeInfinity(value))
{
if (!_failed)
{
_failed = true;
Contracts.Assert(false, string.Format("Double.TryParse failed on: {0}", str));
}
}
}
else if (FloatUtils.GetBits(x) != FloatUtils.GetBits(value))
{
// Double.TryParse gets negative zero wrong!
if (FloatUtils.GetBits(x) != 0 || FloatUtils.GetBits(value) != TopBit || !neg)
{
System.Diagnostics.Debug.WriteLine("*** FloatParser disagrees with Double.TryParse on: {0} ({1} vs {2})", str, FloatUtils.GetBits(x), FloatUtils.GetBits(value));
}
}
#endif
return true;
}
private static bool TryParseSpecial(ReadOnlySpan<char> span, ref int ich, out Double value)
{
Single tmp;
bool res = TryParseSpecial(span, ref ich, out tmp);
value = tmp;
return res;
}
private static bool TryParseSpecial(ReadOnlySpan<char> span, ref int ich, out Single value)
{
if (ich < span.Length)
{
switch (span[ich])
{
case '?':
// We also interpret ? to mean NaN.
value = Single.NaN;
ich += 1;
return true;
case 'N':
if (ich + 3 <= span.Length && span[ich + 1] == 'a' && span[ich + 2] == 'N')
{
value = Single.NaN;
ich += 3;
return true;
}
break;
case 'I':
if (ich + 8 <= span.Length && span[ich + 1] == 'n' && span[ich + 2] == 'f' && span[ich + 3] == 'i' && span[ich + 4] == 'n' && span[ich + 5] == 'i' && span[ich + 6] == 't' && span[ich + 7] == 'y')
{
value = Single.PositiveInfinity;
ich += 8;
return true;
}
break;
case '-':
if (ich + 2 <= span.Length && span[ich + 1] == InfinitySymbol)
{
value = Single.NegativeInfinity;
ich += 2;
return true;
}
if (ich + 9 <= span.Length && span[ich + 1] == 'I' && span[ich + 2] == 'n' && span[ich + 3] == 'f' && span[ich + 4] == 'i' && span[ich + 5] == 'n' && span[ich + 6] == 'i' && span[ich + 7] == 't' && span[ich + 8] == 'y')
{
value = Single.NegativeInfinity;
ich += 9;
return true;
}
break;
case InfinitySymbol:
value = Single.PositiveInfinity;
ich += 1;
return true;
}
}
value = default(Single);
return false;
}
private static bool TryParseCore(ReadOnlySpan<char> span, ref int ich, ref bool neg, ref ulong num, ref long exp, OptionFlags flags = OptionFlags.Default)
{
Contracts.Assert(0 <= ich && ich <= span.Length);
Contracts.Assert(!neg);
Contracts.Assert(num == 0);
Contracts.Assert(exp == 0);
char decimalMarker;
if ((flags & OptionFlags.UseCommaAsDecimalMarker) != 0)
decimalMarker = ',';
else
decimalMarker = '.';
if (ich >= span.Length)
return false;
// If num gets bigger than this, we don't process additional digits.
// REVIEW: Should we ensure that round off is always precise?
const ulong numMax = (ulong.MaxValue - 9) / 10;
bool digits = false;
// Get started: handle sign
int i = ich;
switch (span[i])
{
default:
return false;
case '-':
if (++i >= span.Length)
return false;
neg = true;
break;
case '+':
if (++i >= span.Length)
return false;
break;
case '.':
if (decimalMarker != '.') // Decimal marker was not '.', but we encountered a '.', which must be an error.
return false; // Since this was an error, return false, which will later make the caller to set NaN as the out value.
goto LPoint;
case ',':
if (decimalMarker != ',') // Same logic as above.
return false;
goto LPoint;
// The common cases.
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
}
// Get digits before the decimal marker, which may be '.' or ','
uint d;
for (; ; )
{
Contracts.Assert(i < span.Length);
if ((d = (uint)span[i] - '0') > 9)
break;
digits = true;
if (num < numMax)
num = 10 * num + d;
else
exp++;
if (++i >= span.Length)
{
ich = i;
return true;
}
}
Contracts.Assert(i < span.Length);
if (span[i] != decimalMarker)
goto LAfterDigits;
LPoint:
Contracts.Assert(i < span.Length);
Contracts.Assert(span[i] == decimalMarker);
// Get the digits after the decimal marker, which may be '.' or ','
for (; ; )
{
if (++i >= span.Length)
{
if (digits)
ich = i;
return digits;
}
Contracts.Assert(i < span.Length);
if ((d = (uint)span[i] - '0') > 9)
break;
digits = true;
if (num < numMax)
{
num = 10 * num + d;
exp--;
}
}
LAfterDigits:
Contracts.Assert(i < span.Length);
if (!digits)
return false;
// Remember the current position.
ich = i;
// Check for an exponent.
switch (span[i])
{
default:
return true;
case 'e':
case 'E':
if (++i >= span.Length)
return true;
break;
}
// Handle the exponent sign.
bool expNeg = false;
Contracts.Assert(i < span.Length);
switch (span[i])
{
case '-':
if (++i >= span.Length)
return true;
expNeg = true;
break;
case '+':
if (++i >= span.Length)
return true;
break;
}
// If the exponent exceeds this, the result will be infinite or zero
// (depending on sign), so we clip it (to avoid overflow). Since exp is currently
// bounded by a number of characters (digits before/after the decimal), adding
// it to something bounded by eMax will not overflow.
const long eMax = long.MaxValue / 100;
Contracts.Assert(Math.Abs(exp) < int.MaxValue);
digits = false;
long e = 0;
for (; ; )
{
Contracts.Assert(i < span.Length);
if ((d = (uint)span[i] - '0') > 9)
break;
digits = true;
if (e < eMax)
e = 10 * e + (int)d;
if (++i >= span.Length)
break;
}
if (digits)
{
if (expNeg)
e = -e;
exp += e;
ich = i;
}
return true;
}
// Map from base-10 exponent to 64-bit mantissa.
// The approximation for 10^n is _mpe10man[n-1] * 2^(_mpe10e2[n-1]-64).
private static readonly ulong[] _mpe10Man = new ulong[] {
0xA000000000000000UL, 0xC800000000000000UL, 0xFA00000000000000UL, 0x9C40000000000000UL, 0xC350000000000000UL, /*005*/
0xF424000000000000UL, 0x9896800000000000UL, 0xBEBC200000000000UL, 0xEE6B280000000000UL, 0x9502F90000000000UL, /*010*/
0xBA43B74000000000UL, 0xE8D4A51000000000UL, 0x9184E72A00000000UL, 0xB5E620F480000000UL, 0xE35FA931A0000000UL, /*015*/
0x8E1BC9BF04000000UL, 0xB1A2BC2EC5000000UL, 0xDE0B6B3A76400000UL, 0x8AC7230489E80000UL, 0xAD78EBC5AC620000UL, /*020*/
0xD8D726B7177A8000UL, 0x878678326EAC9000UL, 0xA968163F0A57B400UL, 0xD3C21BCECCEDA100UL, 0x84595161401484A0UL, /*025*/
0xA56FA5B99019A5C8UL, 0xCECB8F27F4200F3AUL, 0x813F3978F8940984UL, 0xA18F07D736B90BE5UL, 0xC9F2C9CD04674EDEUL, /*030*/
0xFC6F7C4045812296UL, 0x9DC5ADA82B70B59DUL, 0xC5371912364CE305UL, 0xF684DF56C3E01BC6UL, 0x9A130B963A6C115CUL, /*035*/
0xC097CE7BC90715B3UL, 0xF0BDC21ABB48DB20UL, 0x96769950B50D88F4UL, 0xBC143FA4E250EB31UL, 0xEB194F8E1AE525FDUL, /*040*/
0x92EFD1B8D0CF37BEUL, 0xB7ABC627050305ADUL, 0xE596B7B0C643C719UL, 0x8F7E32CE7BEA5C6FUL, 0xB35DBF821AE4F38BUL, /*045*/
0xE0352F62A19E306EUL, 0x8C213D9DA502DE45UL, 0xAF298D050E4395D6UL, 0xDAF3F04651D47B4CUL, 0x88D8762BF324CD0FUL, /*050*/
0xAB0E93B6EFEE0053UL, 0xD5D238A4ABE98068UL, 0x85A36366EB71F041UL, 0xA70C3C40A64E6C51UL, 0xD0CF4B50CFE20765UL, /*055*/
0x82818F1281ED449FUL, 0xA321F2D7226895C7UL, 0xCBEA6F8CEB02BB39UL, 0xFEE50B7025C36A08UL, 0x9F4F2726179A2245UL, /*060*/
0xC722F0EF9D80AAD6UL, 0xF8EBAD2B84E0D58BUL, 0x9B934C3B330C8577UL, 0xC2781F49FFCFA6D5UL, 0xF316271C7FC3908AUL, /*065*/
0x97EDD871CFDA3A56UL, 0xBDE94E8E43D0C8ECUL, 0xED63A231D4C4FB27UL, 0x945E455F24FB1CF8UL, 0xB975D6B6EE39E436UL, /*070*/
0xE7D34C64A9C85D44UL, 0x90E40FBEEA1D3A4AUL, 0xB51D13AEA4A488DDUL, 0xE264589A4DCDAB14UL, 0x8D7EB76070A08AECUL, /*075*/
0xB0DE65388CC8ADA8UL, 0xDD15FE86AFFAD912UL, 0x8A2DBF142DFCC7ABUL, 0xACB92ED9397BF996UL, 0xD7E77A8F87DAF7FBUL, /*080*/
0x86F0AC99B4E8DAFDUL, 0xA8ACD7C0222311BCUL, 0xD2D80DB02AABD62BUL, 0x83C7088E1AAB65DBUL, 0xA4B8CAB1A1563F52UL, /*085*/
0xCDE6FD5E09ABCF26UL, 0x80B05E5AC60B6178UL, 0xA0DC75F1778E39D6UL, 0xC913936DD571C84CUL, 0xFB5878494ACE3A5FUL, /*090*/
0x9D174B2DCEC0E47BUL, 0xC45D1DF942711D9AUL, 0xF5746577930D6500UL, 0x9968BF6ABBE85F20UL, 0xBFC2EF456AE276E8UL, /*095*/
0xEFB3AB16C59B14A2UL, 0x95D04AEE3B80ECE5UL, 0xBB445DA9CA61281FUL, 0xEA1575143CF97226UL, 0x924D692CA61BE758UL, /*100*/
0xB6E0C377CFA2E12EUL, 0xE498F455C38B997AUL, 0x8EDF98B59A373FECUL, 0xB2977EE300C50FE7UL, 0xDF3D5E9BC0F653E1UL, /*105*/
0x8B865B215899F46CUL, 0xAE67F1E9AEC07187UL, 0xDA01EE641A708DE9UL, 0x884134FE908658B2UL, 0xAA51823E34A7EEDEUL, /*110*/
0xD4E5E2CDC1D1EA96UL, 0x850FADC09923329EUL, 0xA6539930BF6BFF45UL, 0xCFE87F7CEF46FF16UL, 0x81F14FAE158C5F6EUL, /*115*/
0xA26DA3999AEF7749UL, 0xCB090C8001AB551CUL, 0xFDCB4FA002162A63UL, 0x9E9F11C4014DDA7EUL, 0xC646D63501A1511DUL, /*120*/
0xF7D88BC24209A565UL, 0x9AE757596946075FUL, 0xC1A12D2FC3978937UL, 0xF209787BB47D6B84UL, 0x9745EB4D50CE6332UL, /*125*/
0xBD176620A501FBFFUL, 0xEC5D3FA8CE427AFFUL, 0x93BA47C980E98CDFUL, 0xB8A8D9BBE123F017UL, 0xE6D3102AD96CEC1DUL, /*130*/
0x9043EA1AC7E41392UL, 0xB454E4A179DD1877UL, 0xE16A1DC9D8545E94UL, 0x8CE2529E2734BB1DUL, 0xB01AE745B101E9E4UL, /*135*/
0xDC21A1171D42645DUL, 0x899504AE72497EBAUL, 0xABFA45DA0EDBDE69UL, 0xD6F8D7509292D603UL, 0x865B86925B9BC5C2UL, /*140*/
0xA7F26836F282B732UL, 0xD1EF0244AF2364FFUL, 0x8335616AED761F1FUL, 0xA402B9C5A8D3A6E7UL, 0xCD036837130890A1UL, /*145*/
0x802221226BE55A64UL, 0xA02AA96B06DEB0FDUL, 0xC83553C5C8965D3DUL, 0xFA42A8B73ABBF48CUL, 0x9C69A97284B578D7UL, /*150*/
0xC38413CF25E2D70DUL, 0xF46518C2EF5B8CD1UL, 0x98BF2F79D5993802UL, 0xBEEEFB584AFF8603UL, 0xEEAABA2E5DBF6784UL, /*155*/
0x952AB45CFA97A0B2UL, 0xBA756174393D88DFUL, 0xE912B9D1478CEB17UL, 0x91ABB422CCB812EEUL, 0xB616A12B7FE617AAUL, /*160*/
0xE39C49765FDF9D94UL, 0x8E41ADE9FBEBC27DUL, 0xB1D219647AE6B31CUL, 0xDE469FBD99A05FE3UL, 0x8AEC23D680043BEEUL, /*165*/
0xADA72CCC20054AE9UL, 0xD910F7FF28069DA4UL, 0x87AA9AFF79042286UL, 0xA99541BF57452B28UL, 0xD3FA922F2D1675F2UL, /*170*/
0x847C9B5D7C2E09B7UL, 0xA59BC234DB398C25UL, 0xCF02B2C21207EF2EUL, 0x8161AFB94B44F57DUL, 0xA1BA1BA79E1632DCUL, /*175*/
0xCA28A291859BBF93UL, 0xFCB2CB35E702AF78UL, 0x9DEFBF01B061ADABUL, 0xC56BAEC21C7A1916UL, 0xF6C69A72A3989F5BUL, /*180*/
0x9A3C2087A63F6399UL, 0xC0CB28A98FCF3C7FUL, 0xF0FDF2D3F3C30B9FUL, 0x969EB7C47859E743UL, 0xBC4665B596706114UL, /*185*/
0xEB57FF22FC0C7959UL, 0x9316FF75DD87CBD8UL, 0xB7DCBF5354E9BECEUL, 0xE5D3EF282A242E81UL, 0x8FA475791A569D10UL, /*190*/
0xB38D92D760EC4455UL, 0xE070F78D3927556AUL, 0x8C469AB843B89562UL, 0xAF58416654A6BABBUL, 0xDB2E51BFE9D0696AUL, /*195*/
0x88FCF317F22241E2UL, 0xAB3C2FDDEEAAD25AUL, 0xD60B3BD56A5586F1UL, 0x85C7056562757456UL, 0xA738C6BEBB12D16CUL, /*200*/
0xD106F86E69D785C7UL, 0x82A45B450226B39CUL, 0xA34D721642B06084UL, 0xCC20CE9BD35C78A5UL, 0xFF290242C83396CEUL, /*205*/
0x9F79A169BD203E41UL, 0xC75809C42C684DD1UL, 0xF92E0C3537826145UL, 0x9BBCC7A142B17CCBUL, 0xC2ABF989935DDBFEUL, /*210*/
0xF356F7EBF83552FEUL, 0x98165AF37B2153DEUL, 0xBE1BF1B059E9A8D6UL, 0xEDA2EE1C7064130CUL, 0x9485D4D1C63E8BE7UL, /*215*/
0xB9A74A0637CE2EE1UL, 0xE8111C87C5C1BA99UL, 0x910AB1D4DB9914A0UL, 0xB54D5E4A127F59C8UL, 0xE2A0B5DC971F303AUL, /*220*/
0x8DA471A9DE737E24UL, 0xB10D8E1456105DADUL, 0xDD50F1996B947518UL, 0x8A5296FFE33CC92FUL, 0xACE73CBFDC0BFB7BUL, /*225*/
0xD8210BEFD30EFA5AUL, 0x8714A775E3E95C78UL, 0xA8D9D1535CE3B396UL, 0xD31045A8341CA07CUL, 0x83EA2B892091E44DUL, /*230*/
0xA4E4B66B68B65D60UL, 0xCE1DE40642E3F4B9UL, 0x80D2AE83E9CE78F3UL, 0xA1075A24E4421730UL, 0xC94930AE1D529CFCUL, /*235*/
0xFB9B7CD9A4A7443CUL, 0x9D412E0806E88AA5UL, 0xC491798A08A2AD4EUL, 0xF5B5D7EC8ACB58A2UL, 0x9991A6F3D6BF1765UL, /*240*/
0xBFF610B0CC6EDD3FUL, 0xEFF394DCFF8A948EUL, 0x95F83D0A1FB69CD9UL, 0xBB764C4CA7A4440FUL, 0xEA53DF5FD18D5513UL, /*245*/
0x92746B9BE2F8552CUL, 0xB7118682DBB66A77UL, 0xE4D5E82392A40515UL, 0x8F05B1163BA6832DUL, 0xB2C71D5BCA9023F8UL, /*250*/
0xDF78E4B2BD342CF6UL, 0x8BAB8EEFB6409C1AUL, 0xAE9672ABA3D0C320UL, 0xDA3C0F568CC4F3E8UL, 0x8865899617FB1871UL, /*255*/
0xAA7EEBFB9DF9DE8DUL, 0xD51EA6FA85785631UL, 0x8533285C936B35DEUL, 0xA67FF273B8460356UL, 0xD01FEF10A657842CUL, /*260*/
0x8213F56A67F6B29BUL, 0xA298F2C501F45F42UL, 0xCB3F2F7642717713UL, 0xFE0EFB53D30DD4D7UL, 0x9EC95D1463E8A506UL, /*265*/
0xC67BB4597CE2CE48UL, 0xF81AA16FDC1B81DAUL, 0x9B10A4E5E9913128UL, 0xC1D4CE1F63F57D72UL, 0xF24A01A73CF2DCCFUL, /*270*/
0x976E41088617CA01UL, 0xBD49D14AA79DBC82UL, 0xEC9C459D51852BA2UL, 0x93E1AB8252F33B45UL, 0xB8DA1662E7B00A17UL, /*275*/
0xE7109BFBA19C0C9DUL, 0x906A617D450187E2UL, 0xB484F9DC9641E9DAUL, 0xE1A63853BBD26451UL, 0x8D07E33455637EB2UL, /*280*/
0xB049DC016ABC5E5FUL, 0xDC5C5301C56B75F7UL, 0x89B9B3E11B6329BAUL, 0xAC2820D9623BF429UL, 0xD732290FBACAF133UL, /*285*/
0x867F59A9D4BED6C0UL, 0xA81F301449EE8C70UL, 0xD226FC195C6A2F8CUL, 0x83585D8FD9C25DB7UL, 0xA42E74F3D032F525UL, /*290*/
0xCD3A1230C43FB26FUL, 0x80444B5E7AA7CF85UL, 0xA0555E361951C366UL, 0xC86AB5C39FA63440UL, 0xFA856334878FC150UL, /*295*/
0x9C935E00D4B9D8D2UL, 0xC3B8358109E84F07UL, 0xF4A642E14C6262C8UL, 0x98E7E9CCCFBD7DBDUL, 0xBF21E44003ACDD2CUL, /*300*/
0xEEEA5D5004981478UL, 0x95527A5202DF0CCBUL, 0xBAA718E68396CFFDUL, 0xE950DF20247C83FDUL, 0x91D28B7416CDD27EUL, /*305*/
0xB6472E511C81471DUL, 0xE3D8F9E563A198E5UL, 0x8E679C2F5E44FF8FUL, 0xB201833B35D63F73UL, 0xDE81E40A034BCF4FUL, /*310*/
0x8B112E86420F6191UL, 0xADD57A27D29339F6UL, 0xD94AD8B1C7380874UL, 0x87CEC76F1C830548UL, 0xA9C2794AE3A3C69AUL, /*315*/
0xD433179D9C8CB841UL, 0x849FEEC281D7F328UL, 0xA5C7EA73224DEFF3UL, 0xCF39E50FEAE16BEFUL, 0x81842F29F2CCE375UL, /*320*/
};
// Map from negative base-10 exponent to 64-bit mantissa. Note that the top bit of these is set.
// The approximation for 10^-n is _mpne10man[n-1] * 2^(-_mpne10ne2[n-1]-64).
private static readonly ulong[] _mpne10Man = new ulong[] {
0xCCCCCCCCCCCCCCCDUL, 0xA3D70A3D70A3D70AUL, 0x83126E978D4FDF3BUL, 0xD1B71758E219652CUL, 0xA7C5AC471B478423UL, /*005*/
0x8637BD05AF6C69B6UL, 0xD6BF94D5E57A42BCUL, 0xABCC77118461CEFDUL, 0x89705F4136B4A597UL, 0xDBE6FECEBDEDD5BFUL, /*010*/
0xAFEBFF0BCB24AAFFUL, 0x8CBCCC096F5088CCUL, 0xE12E13424BB40E13UL, 0xB424DC35095CD80FUL, 0x901D7CF73AB0ACD9UL, /*015*/
0xE69594BEC44DE15BUL, 0xB877AA3236A4B449UL, 0x9392EE8E921D5D07UL, 0xEC1E4A7DB69561A5UL, 0xBCE5086492111AEBUL, /*020*/
0x971DA05074DA7BEFUL, 0xF1C90080BAF72CB1UL, 0xC16D9A0095928A27UL, 0x9ABE14CD44753B53UL, 0xF79687AED3EEC551UL, /*025*/
0xC612062576589DDBUL, 0x9E74D1B791E07E48UL, 0xFD87B5F28300CA0EUL, 0xCAD2F7F5359A3B3EUL, 0xA2425FF75E14FC32UL, /*030*/
0x81CEB32C4B43FCF5UL, 0xCFB11EAD453994BAUL, 0xA6274BBDD0FADD62UL, 0x84EC3C97DA624AB5UL, 0xD4AD2DBFC3D07788UL, /*035*/
0xAA242499697392D3UL, 0x881CEA14545C7575UL, 0xD9C7DCED53C72256UL, 0xAE397D8AA96C1B78UL, 0x8B61313BBABCE2C6UL, /*040*/
0xDF01E85F912E37A3UL, 0xB267ED1940F1C61CUL, 0x8EB98A7A9A5B04E3UL, 0xE45C10C42A2B3B06UL, 0xB6B00D69BB55C8D1UL, /*045*/
0x9226712162AB070EUL, 0xE9D71B689DDE71B0UL, 0xBB127C53B17EC159UL, 0x95A8637627989AAEUL, 0xEF73D256A5C0F77DUL, /*050*/
0xBF8FDB78849A5F97UL, 0x993FE2C6D07B7FACUL, 0xF53304714D9265E0UL, 0xC428D05AA4751E4DUL, 0x9CED737BB6C4183DUL, /*055*/
0xFB158592BE068D2FUL, 0xC8DE047564D20A8CUL, 0xA0B19D2AB70E6ED6UL, 0x808E17555F3EBF12UL, 0xCDB02555653131B6UL, /*060*/
0xA48CEAAAB75A8E2BUL, 0x83A3EEEEF9153E89UL, 0xD29FE4B18E88640FUL, 0xA87FEA27A539E9A5UL, 0x86CCBB52EA94BAEBUL, /*065*/
0xD7ADF884AA879177UL, 0xAC8B2D36EED2DAC6UL, 0x8A08F0F8BF0F156BUL, 0xDCDB1B2798182245UL, 0xB0AF48EC79ACE837UL, /*070*/
0x8D590723948A535FUL, 0xE2280B6C20DD5232UL, 0xB4ECD5F01A4AA828UL, 0x90BD77F3483BB9BAUL, 0xE7958CB87392C2C3UL, /*075*/
0xB94470938FA89BCFUL, 0x9436C0760C86E30CUL, 0xED246723473E3813UL, 0xBDB6B8E905CB600FUL, 0x97C560BA6B0919A6UL, /*080*/
0xF2D56790AB41C2A3UL, 0xC24452DA229B021CUL, 0x9B69DBE1B548CE7DUL, 0xF8A95FCF88747D94UL, 0xC6EDE63FA05D3144UL, /*085*/
0x9F24B832E6B0F436UL, 0xFEA126B7D78186BDUL, 0xCBB41EF979346BCAUL, 0xA2F67F2DFA90563BUL, 0x825ECC24C8737830UL, /*090*/
0xD097AD07A71F26B2UL, 0xA6DFBD9FB8E5B88FUL, 0x857FCAE62D8493A5UL, 0xD59944A37C0752A2UL, 0xAAE103B5FCD2A882UL, /*095*/
0x88B402F7FD75539BUL, 0xDAB99E59958885C5UL, 0xAEFAE51477A06B04UL, 0x8BFBEA76C619EF36UL, 0xDFF9772470297EBDUL, /*100*/
0xB32DF8E9F3546564UL, 0x8F57FA54C2A9EAB7UL, 0xE55990879DDCAABEUL, 0xB77ADA0617E3BBCBUL, 0x92C8AE6B464FC96FUL, /*105*/
0xEADAB0ABA3B2DBE5UL, 0xBBE226EFB628AFEBUL, 0x964E858C91BA2655UL, 0xF07DA27A82C37088UL, 0xC06481FB9BCF8D3AUL, /*110*/
0x99EA0196163FA42EUL, 0xF64335BCF065D37DUL, 0xC5029163F384A931UL, 0x9D9BA7832936EDC1UL, 0xFC2C3F3841F17C68UL, /*115*/
0xC9BCFF6034C13053UL, 0xA163FF802A3426A9UL, 0x811CCC668829B887UL, 0xCE947A3DA6A9273EUL, 0xA54394FE1EEDB8FFUL, /*120*/
0x843610CB4BF160CCUL, 0xD389B47879823479UL, 0xA93AF6C6C79B5D2EUL, 0x87625F056C7C4A8BUL, 0xD89D64D57A607745UL, /*125*/
0xAD4AB7112EB3929EUL, 0x8AA22C0DBEF60EE4UL, 0xDDD0467C64BCE4A1UL, 0xB1736B96B6FD83B4UL, 0x8DF5EFABC5979C90UL, /*130*/
0xE3231912D5BF60E6UL, 0xB5B5ADA8AAFF80B8UL, 0x915E2486EF32CD60UL, 0xE896A0D7E51E1566UL, 0xBA121A4650E4DDECUL, /*135*/
0x94DB483840B717F0UL, 0xEE2BA6C0678B597FUL, 0xBE89523386091466UL, 0x986DDB5C6B3A76B8UL, 0xF3E2F893DEC3F126UL, /*140*/
0xC31BFA0FE5698DB8UL, 0x9C1661A651213E2DUL, 0xF9BD690A1B68637BUL, 0xC7CABA6E7C5382C9UL, 0x9FD561F1FD0F9BD4UL, /*145*/
0xFFBBCFE994E5C620UL, 0xCC963FEE10B7D1B3UL, 0xA3AB66580D5FDAF6UL, 0x82EF85133DE648C5UL, 0xD17F3B51FCA3A7A1UL, /*150*/
0xA798FC4196E952E7UL, 0x8613FD0145877586UL, 0xD686619BA27255A3UL, 0xAB9EB47C81F5114FUL, 0x894BC396CE5DA772UL, /*155*/
0xDBAC6C247D62A584UL, 0xAFBD2350644EEAD0UL, 0x8C974F7383725573UL, 0xE0F218B8D25088B8UL, 0xB3F4E093DB73A093UL, /*160*/
0x8FF71A0FE2C2E6DCUL, 0xE65829B3046B0AFAUL, 0xB84687C269EF3BFBUL, 0x936B9FCEBB25C996UL, 0xEBDF661791D60F56UL, /*165*/
0xBCB2B812DB11A5DEUL, 0x96F5600F15A7B7E5UL, 0xF18899B1BC3F8CA2UL, 0xC13A148E3032D6E8UL, 0x9A94DD3E8CF578BAUL, /*170*/
0xF7549530E188C129UL, 0xC5DD44271AD3CDBAUL, 0x9E4A9CEC15763E2FUL, 0xFD442E4688BD304BUL, 0xCA9CF1D206FDC03CUL, /*175*/
0xA21727DB38CB0030UL, 0x81AC1FE293D599C0UL, 0xCF79CC9DB955C2CCUL, 0xA5FB0A17C777CF0AUL, 0x84C8D4DFD2C63F3BUL, /*180*/
0xD47487CC8470652BUL, 0xA9F6D30A038D1DBCUL, 0x87F8A8D4CFA417CAUL, 0xD98DDAEE19068C76UL, 0xAE0B158B4738705FUL, /*185*/
0x8B3C113C38F9F37FUL, 0xDEC681F9F4C31F31UL, 0xB23867FB2A35B28EUL, 0x8E938662882AF53EUL, 0xE41F3D6A7377EECAUL, /*190*/
0xB67F6455292CBF08UL, 0x91FF83775423CC06UL, 0xE998D258869FACD7UL, 0xBAE0A846D2195713UL, 0x9580869F0E7AAC0FUL, /*195*/
0xEF340A98172AACE5UL, 0xBF5CD54678EEF0B7UL, 0x991711052D8BF3C5UL, 0xF4F1B4D515ACB93CUL, 0xC3F490AA77BD60FDUL, /*200*/
0x9CC3A6EEC6311A64UL, 0xFAD2A4B13D1B5D6CUL, 0xC8A883C0FDAF7DF0UL, 0xA086CFCD97BF97F4UL, 0x806BD9714632DFF6UL, /*205*/
0xCD795BE870516656UL, 0xA46116538D0DEB78UL, 0x8380DEA93DA4BC60UL, 0xD267CAA862A12D67UL, 0xA8530886B54DBDECUL, /*210*/
0x86A8D39EF77164BDUL, 0xD77485CB25823AC7UL, 0xAC5D37D5B79B6239UL, 0x89E42CAAF9491B61UL, 0xDCA04777F541C568UL, /*215*/
0xB080392CC4349DEDUL, 0x8D3360F09CF6E4BDUL, 0xE1EBCE4DC7F16DFCUL, 0xB4BCA50B065ABE63UL, 0x9096EA6F3848984FUL, /*220*/
0xE757DD7EC07426E5UL, 0xB913179899F68584UL, 0x940F4613AE5ED137UL, 0xECE53CEC4A314EBEUL, 0xBD8430BD08277231UL, /*225*/
0x979CF3CA6CEC5B5BUL, 0xF294B943E17A2BC4UL, 0xC21094364DFB5637UL, 0x9B407691D7FC44F8UL, 0xF867241C8CC6D4C1UL, /*230*/
0xC6B8E9B0709F109AUL, 0x9EFA548D26E5A6E2UL, 0xFE5D54150B090B03UL, 0xCB7DDCDDA26DA269UL, 0xA2CB1717B52481EDUL, /*235*/
0x823C12795DB6CE57UL, 0xD0601D8EFC57B08CUL, 0xA6B34AD8C9DFC070UL, 0x855C3BE0A17FCD26UL, 0xD5605FCDCF32E1D7UL, /*240*/
0xAAB37FD7D8F58179UL, 0x888F99797A5E012DUL, 0xDA7F5BF590966849UL, 0xAECC49914078536DUL, 0x8BD6A141006042BEUL, /*245*/
0xDFBDCECE67006AC9UL, 0xB2FE3F0B8599EF08UL, 0x8F31CC0937AE58D3UL, 0xE51C79A85916F485UL, 0xB749FAED14125D37UL, /*250*/
0x92A1958A7675175FUL, 0xEA9C227723EE8BCBUL, 0xBBB01B9283253CA3UL, 0x96267C7535B763B5UL, 0xF03D93EEBC589F88UL, /*255*/
0xC0314325637A193AUL, 0x99C102844F94E0FBUL, 0xF6019DA07F549B2BUL, 0xC4CE17B399107C23UL, 0x9D71AC8FADA6C9B5UL, /*260*/
0xFBE9141915D7A922UL, 0xC987434744AC874FUL, 0xA139029F6A239F72UL, 0x80FA687F881C7F8EUL, 0xCE5D73FF402D98E4UL, /*265*/
0xA5178FFF668AE0B6UL, 0x8412D9991ED58092UL, 0xD3515C2831559A83UL, 0xA90DE3535AAAE202UL, 0x873E4F75E2224E68UL, /*270*/
0xD863B256369D4A41UL, 0xAD1C8EAB5EE43B67UL, 0x8A7D3EEF7F1CFC52UL, 0xDD95317F31C7FA1DUL, 0xB1442798F49FFB4BUL, /*275*/
0x8DD01FAD907FFC3CUL, 0xE2E69915B3FFF9F9UL, 0xB58547448FFFFB2EUL, 0x91376C36D99995BEUL, 0xE858AD248F5C22CAUL, /*280*/
0xB9E08A83A5E34F08UL, 0x94B3A202EB1C3F39UL, 0xEDEC366B11C6CB8FUL, 0xBE5691EF416BD60CUL, 0x9845418C345644D7UL, /*285*/
0xF3A20279ED56D48AUL, 0xC2E801FB244576D5UL, 0x9BECCE62836AC577UL, 0xF97AE3D0D2446F25UL, 0xC795830D75038C1EUL, /*290*/
0x9FAACF3DF73609B1UL, 0xFF77B1FCBEBCDC4FUL, 0xCC5FC196FEFD7D0CUL, 0xA37FCE126597973DUL, 0x82CCA4DB847945CAUL, /*295*/
0xD1476E2C07286FAAUL, 0xA76C582338ED2622UL, 0x85F0468293F0EB4EUL, 0xD64D3D9DB981787DUL, 0xAB70FE17C79AC6CAUL, /*300*/
0x892731AC9FAF056FUL, 0xDB71E91432B1A24BUL, 0xAF8E5410288E1B6FUL, 0x8C71DCD9BA0B4926UL, 0xE0B62E2929ABA83CUL, /*305*/
0xB3C4F1BA87BC8697UL, 0x8FD0C16206306BACUL, 0xE61ACF033D1A45DFUL, 0xB8157268FDAE9E4CUL, 0x93445B8731587EA3UL, /*310*/
0xEBA09271E88D976CUL, 0xBC807527ED3E12BDUL, 0x96CD2A865764DBCAUL, 0xF148440A256E2C77UL, 0xC1069CD4EABE89F9UL, /*315*/
0x9A6BB0AA55653B2DUL, 0xF712B443BBD52B7CUL, 0xC5A890362FDDBC63UL, 0x9E20735E8CB16382UL, 0xFD00B897478238D1UL, /*320*/
0xCA66FA129F9B60A7UL, 0xA1EBFB4219491A1FUL, 0x818995CE7AA0E1B2UL, 0xCF42894A5DCE35EAUL, 0xA5CED43B7E3E9188UL, /*325*/
0x84A57695FE98746DUL, 0xD43BF0EFFDC0BA48UL, 0xA9C98D8CCB009506UL, 0x87D4713D6F33AA6CUL, 0xD953E8624B85DD79UL, /*330*/
0xADDCB9E83C6B1794UL, 0x8B16FB203055AC76UL, 0xDE8B2B66B3BC4724UL, 0xB208EF855C969F50UL, 0x8E6D8C6AB0787F73UL, /*335*/
0xE3E27A444D8D98B8UL, 0xB64EC836A47146FAUL, 0x91D8A02BB6C10594UL, 0xE95A99DF8ACE6F54UL, 0xBAAEE17FA23EBF76UL, /*340*/
0x9558B4661B6565F8UL, 0xEEF453D6923BD65AUL, 0xBF29DCABA82FDEAEUL, 0x98EE4A22ECF3188CUL, 0xF4B0769E47EB5A79UL, /*345*/
0xC3C05EE50655E1FAUL, 0x9C99E58405118195UL, 0xFA8FD5A0081C0288UL, 0xC873114CD3499BA0UL, 0xA05C0DD70F6E161AUL, /*350*/
0x8049A4AC0C5811AEUL, 0xCD42A11346F34F7DUL, 0xA4354DA9058F72CAUL, 0x835DD7BA6AD928A2UL, 0xD22FBF90AAF50DD0UL, /*355*/
0xA82632DA225DA4A6UL, 0x8684F57B4EB15085UL, 0xD73B225EE44EE73BUL, 0xAC2F4EB2503F1F63UL, 0x89BF722840327F82UL, /*360*/
};
// Map from base-10 exponent to base-2 exponent.
// The approximation for 10^n is _mpe10man[n-1] * 2^(_mpe10e2[n-1]-64).
private static readonly short[] _mpe10e2 = new short[] {
4, 7, 10, 14, 17, 20, 24, 27, 30, 34, 37, 40, 44, 47, 50, 54, 57, 60, 64, 67, /*020*/
70, 74, 77, 80, 84, 87, 90, 94, 97, 100, 103, 107, 110, 113, 117, 120, 123, 127, 130, 133, /*040*/
137, 140, 143, 147, 150, 153, 157, 160, 163, 167, 170, 173, 177, 180, 183, 187, 190, 193, 196, 200, /*060*/
203, 206, 210, 213, 216, 220, 223, 226, 230, 233, 236, 240, 243, 246, 250, 253, 256, 260, 263, 266, /*080*/
270, 273, 276, 280, 283, 286, 290, 293, 296, 299, 303, 306, 309, 313, 316, 319, 323, 326, 329, 333, /*100*/
336, 339, 343, 346, 349, 353, 356, 359, 363, 366, 369, 373, 376, 379, 383, 386, 389, 392, 396, 399, /*120*/
402, 406, 409, 412, 416, 419, 422, 426, 429, 432, 436, 439, 442, 446, 449, 452, 456, 459, 462, 466, /*140*/
469, 472, 476, 479, 482, 486, 489, 492, 495, 499, 502, 505, 509, 512, 515, 519, 522, 525, 529, 532, /*160*/
535, 539, 542, 545, 549, 552, 555, 559, 562, 565, 569, 572, 575, 579, 582, 585, 588, 592, 595, 598, /*180*/
602, 605, 608, 612, 615, 618, 622, 625, 628, 632, 635, 638, 642, 645, 648, 652, 655, 658, 662, 665, /*200*/
668, 672, 675, 678, 681, 685, 688, 691, 695, 698, 701, 705, 708, 711, 715, 718, 721, 725, 728, 731, /*220*/
735, 738, 741, 745, 748, 751, 755, 758, 761, 765, 768, 771, 775, 778, 781, 784, 788, 791, 794, 798, /*240*/
801, 804, 808, 811, 814, 818, 821, 824, 828, 831, 834, 838, 841, 844, 848, 851, 854, 858, 861, 864, /*260*/
868, 871, 874, 877, 881, 884, 887, 891, 894, 897, 901, 904, 907, 911, 914, 917, 921, 924, 927, 931, /*280*/
934, 937, 941, 944, 947, 951, 954, 957, 961, 964, 967, 971, 974, 977, 980, 984, 987, 990, 994, 997, /*300*/
1000, 1004, 1007, 1010, 1014, 1017, 1020, 1024, 1027, 1030, 1034, 1037, 1040, 1044, 1047, 1050, 1054, 1057, 1060, 1064, /*320*/
};
// Map from negative base-10 exponent to negative base-2 exponent.
// The approximation for 10^-n is _mpne10man[n-1] * 2^(-_mpne10ne2[n-1]-64).
private static readonly short[] _mpne10ne2 = new short[] {
3, 6, 9, 13, 16, 19, 23, 26, 29, 33, 36, 39, 43, 46, 49, 53, 56, 59, 63, 66, /*020*/
69, 73, 76, 79, 83, 86, 89, 93, 96, 99, 102, 106, 109, 112, 116, 119, 122, 126, 129, 132, /*040*/
136, 139, 142, 146, 149, 152, 156, 159, 162, 166, 169, 172, 176, 179, 182, 186, 189, 192, 195, 199, /*060*/
202, 205, 209, 212, 215, 219, 222, 225, 229, 232, 235, 239, 242, 245, 249, 252, 255, 259, 262, 265, /*080*/
269, 272, 275, 279, 282, 285, 289, 292, 295, 298, 302, 305, 308, 312, 315, 318, 322, 325, 328, 332, /*100*/
335, 338, 342, 345, 348, 352, 355, 358, 362, 365, 368, 372, 375, 378, 382, 385, 388, 391, 395, 398, /*120*/
401, 405, 408, 411, 415, 418, 421, 425, 428, 431, 435, 438, 441, 445, 448, 451, 455, 458, 461, 465, /*140*/
468, 471, 475, 478, 481, 485, 488, 491, 494, 498, 501, 504, 508, 511, 514, 518, 521, 524, 528, 531, /*160*/
534, 538, 541, 544, 548, 551, 554, 558, 561, 564, 568, 571, 574, 578, 581, 584, 587, 591, 594, 597, /*180*/
601, 604, 607, 611, 614, 617, 621, 624, 627, 631, 634, 637, 641, 644, 647, 651, 654, 657, 661, 664, /*200*/
667, 671, 674, 677, 680, 684, 687, 690, 694, 697, 700, 704, 707, 710, 714, 717, 720, 724, 727, 730, /*220*/
734, 737, 740, 744, 747, 750, 754, 757, 760, 764, 767, 770, 774, 777, 780, 783, 787, 790, 793, 797, /*240*/
800, 803, 807, 810, 813, 817, 820, 823, 827, 830, 833, 837, 840, 843, 847, 850, 853, 857, 860, 863, /*260*/
867, 870, 873, 876, 880, 883, 886, 890, 893, 896, 900, 903, 906, 910, 913, 916, 920, 923, 926, 930, /*280*/
933, 936, 940, 943, 946, 950, 953, 956, 960, 963, 966, 970, 973, 976, 979, 983, 986, 989, 993, 996, /*300*/
999, 1003, 1006, 1009, 1013, 1016, 1019, 1023, 1026, 1029, 1033, 1036, 1039, 1043, 1046, 1049, 1053, 1056, 1059, 1063, /*320*/
1066, 1069, 1072, 1076, 1079, 1082, 1086, 1089, 1092, 1096, 1099, 1102, 1106, 1109, 1112, 1116, 1119, 1122, 1126, 1129, /*340*/
1132, 1136, 1139, 1142, 1146, 1149, 1152, 1156, 1159, 1162, 1165, 1169, 1172, 1175, 1179, 1182, 1185, 1189, 1192, 1195, /*360*/
};
// Map from base-10 exponent to Double. Note that since this table is only used for the Single side,
// we don't need all the values - only until the value cast to Single is infinite.
private static readonly Double[] _mpe10Dbl;
// Map from negative base-10 exponent to Double. Note that since this table is only used for the Single side,
// we don't need all the values - only until the value times 2^64 cast to Single is zero.
private static readonly Double[] _mpne10Dbl;
// Build the Double tables from the mantissa/exponent tables.
static DoubleParser()
{
Contracts.Assert(_mpe10Man.Length == _mpe10e2.Length);
Contracts.Assert(_mpne10Man.Length == _mpne10ne2.Length);
// Initialize the Double valued tables.
_mpe10Dbl = new Double[39];
Contracts.Assert(_mpe10Dbl.Length <= _mpe10Man.Length);
for (int i = 0; i < _mpe10Dbl.Length; i++)
{
ulong man = _mpe10Man[i];
// Adjust so the high bit is clear so we can use long => Double. For 10^27 and beyond
// man will not have all the bits, hence we or in 1 (to make sure rounding is correct).
man >>= 1;
if (i >= 26)
man |= 1;
Double dbl = (Double)(ulong)man;
int e2 = _mpe10e2[i] + (0x3FF - 63);
Contracts.Assert(0 < e2 && e2 < 0x7FF);
ulong mul = (ulong)e2 << 52;
unsafe { dbl *= *(Double*)&mul; }
_mpe10Dbl[i] = dbl;
}
Contracts.Assert((Single)_mpe10Dbl[_mpe10Dbl.Length - 1] == Single.PositiveInfinity);
Contracts.Assert((Single)_mpe10Dbl[_mpe10Dbl.Length - 2] < Single.PositiveInfinity);
// Note that since this table is only used for the Single side, we don't need
// any denormals - we go straight from a biased exponent of 1 to zero.
_mpne10Dbl = new Double[65];
Contracts.Assert(_mpne10Dbl.Length <= _mpne10Man.Length);
for (int i = 0; i < _mpne10Dbl.Length; i++)
{
Double dbl = _mpne10Man[i];
int e2 = -_mpne10ne2[i] + (0x3FF - 64);
Contracts.Assert(0 < e2 && e2 < 0x7FF);
ulong mul = (ulong)e2 << 52;
unsafe { dbl *= *(Double*)&mul; }
_mpne10Dbl[i] = dbl;
}
#if DEBUG
Double two64 = (Double)(1UL << 32) * (Double)(1UL << 32);
Contracts.Assert((Single)(_mpne10Dbl[_mpne10Dbl.Length - 1] * two64) == 0);
Contracts.Assert((Single)(_mpne10Dbl[_mpne10Dbl.Length - 2] * two64) > 0);
#endif
}
}
}
|