Use manual packing of IEEE754 double in ConverterUtils.DoubleTryParse()

This commit is contained in:
Alexey Skalozub
2016-09-06 22:02:56 +03:00
parent 7fa2cde56d
commit 20bbadeb0e
+244 -80
View File
@@ -929,46 +929,248 @@ namespace Newtonsoft.Json.Utilities
return ParseResult.Success;
}
private static class IEEE754
{
/// <summary>
/// Exponents for both powers of 10 and 0.1
/// </summary>
private static readonly int[] MultExp64Power10 = new int[]
{
4, 7, 10, 14, 17, 20, 24, 27, 30, 34, 37, 40, 44, 47, 50
};
private const int MaxExponent = 308;
private static readonly double[] DoubleExponents =
{
1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20,
1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, 1e40,
1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 1e60,
1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, 1e80,
1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, 1e100,
1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 1e120,
1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 1e140,
1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 1e160,
1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, 1e169, 1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, 1e179, 1e180,
1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, 1e188, 1e189, 1e190, 1e191, 1e192, 1e193, 1e194, 1e195, 1e196, 1e197, 1e198, 1e199, 1e200,
1e201, 1e202, 1e203, 1e204, 1e205, 1e206, 1e207, 1e208, 1e209, 1e210, 1e211, 1e212, 1e213, 1e214, 1e215, 1e216, 1e217, 1e218, 1e219, 1e220,
1e221, 1e222, 1e223, 1e224, 1e225, 1e226, 1e227, 1e228, 1e229, 1e230, 1e231, 1e232, 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239, 1e240,
1e241, 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249, 1e250, 1e251, 1e252, 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259, 1e260,
1e261, 1e262, 1e263, 1e264, 1e265, 1e266, 1e267, 1e268, 1e269, 1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, 1e277, 1e278, 1e279, 1e280,
1e281, 1e282, 1e283, 1e284, 1e285, 1e286, 1e287, 1e288, 1e289, 1e290, 1e291, 1e292, 1e293, 1e294, 1e295, 1e296, 1e297, 1e298, 1e299, 1e300,
1e301, 1e302, 1e303, 1e304, 1e305, 1e306, 1e307, 1e308
};
private static readonly double[] DoubleNegativeExponents =
{
1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19, 1e-20,
1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28, 1e-29, 1e-30, 1e-31, 1e-32, 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38, 1e-39, 1e-40,
1e-41, 1e-42, 1e-43, 1e-44, 1e-45, 1e-46, 1e-47, 1e-48, 1e-49, 1e-50, 1e-51, 1e-52, 1e-53, 1e-54, 1e-55, 1e-56, 1e-57, 1e-58, 1e-59, 1e-60,
1e-61, 1e-62, 1e-63, 1e-64, 1e-65, 1e-66, 1e-67, 1e-68, 1e-69, 1e-70, 1e-71, 1e-72, 1e-73, 1e-74, 1e-75, 1e-76, 1e-77, 1e-78, 1e-79, 1e-80,
1e-81, 1e-82, 1e-83, 1e-84, 1e-85, 1e-86, 1e-87, 1e-88, 1e-89, 1e-90, 1e-91, 1e-92, 1e-93, 1e-94, 1e-95, 1e-96, 1e-97, 1e-98, 1e-99, 1e-100,
1e-101, 1e-102, 1e-103, 1e-104, 1e-105, 1e-106, 1e-107, 1e-108, 1e-109, 1e-110, 1e-111, 1e-112, 1e-113, 1e-114, 1e-115, 1e-116, 1e-117, 1e-118, 1e-119, 1e-120,
1e-121, 1e-122, 1e-123, 1e-124, 1e-125, 1e-126, 1e-127, 1e-128, 1e-129, 1e-130, 1e-131, 1e-132, 1e-133, 1e-134, 1e-135, 1e-136, 1e-137, 1e-138, 1e-139, 1e-140,
1e-141, 1e-142, 1e-143, 1e-144, 1e-145, 1e-146, 1e-147, 1e-148, 1e-149, 1e-150, 1e-151, 1e-152, 1e-153, 1e-154, 1e-155, 1e-156, 1e-157, 1e-158, 1e-159, 1e-160,
1e-161, 1e-162, 1e-163, 1e-164, 1e-165, 1e-166, 1e-167, 1e-168, 1e-169, 1e-170, 1e-171, 1e-172, 1e-173, 1e-174, 1e-175, 1e-176, 1e-177, 1e-178, 1e-179, 1e-180,
1e-181, 1e-182, 1e-183, 1e-184, 1e-185, 1e-186, 1e-187, 1e-188, 1e-189, 1e-190, 1e-191, 1e-192, 1e-193, 1e-194, 1e-195, 1e-196, 1e-197, 1e-198, 1e-199, 1e-200,
1e-201, 1e-202, 1e-203, 1e-204, 1e-205, 1e-206, 1e-207, 1e-208, 1e-209, 1e-210, 1e-211, 1e-212, 1e-213, 1e-214, 1e-215, 1e-216, 1e-217, 1e-218, 1e-219, 1e-220,
1e-221, 1e-222, 1e-223, 1e-224, 1e-225, 1e-226, 1e-227, 1e-228, 1e-229, 1e-230, 1e-231, 1e-232, 1e-233, 1e-234, 1e-235, 1e-236, 1e-237, 1e-238, 1e-239, 1e-240,
1e-241, 1e-242, 1e-243, 1e-244, 1e-245, 1e-246, 1e-247, 1e-248, 1e-249, 1e-250, 1e-251, 1e-252, 1e-253, 1e-254, 1e-255, 1e-256, 1e-257, 1e-258, 1e-259, 1e-260,
1e-261, 1e-262, 1e-263, 1e-264, 1e-265, 1e-266, 1e-267, 1e-268, 1e-269, 1e-270, 1e-271, 1e-272, 1e-273, 1e-274, 1e-275, 1e-276, 1e-277, 1e-278, 1e-279, 1e-280,
1e-281, 1e-282, 1e-283, 1e-284, 1e-285, 1e-286, 1e-287, 1e-288, 1e-289, 1e-290, 1e-291, 1e-292, 1e-293, 1e-294, 1e-295, 1e-296, 1e-297, 1e-298, 1e-299, 1e-300,
1e-301, 1e-302, 1e-303, 1e-304, 1e-305, 1e-306, 1e-307, 1e-308
};
/// <summary>
/// Normalized powers of 10
/// </summary>
private static readonly ulong[] MultVal64Power10 = new ulong[]
{
0xa000000000000000, 0xc800000000000000, 0xfa00000000000000,
0x9c40000000000000, 0xc350000000000000, 0xf424000000000000,
0x9896800000000000, 0xbebc200000000000, 0xee6b280000000000,
0x9502f90000000000, 0xba43b74000000000, 0xe8d4a51000000000,
0x9184e72a00000000, 0xb5e620f480000000, 0xe35fa931a0000000,
};
/// <summary>
/// Normalized powers of 0.1
/// </summary>
private static readonly ulong[] MultVal64Power10Inv = new ulong[]
{
0xcccccccccccccccd, 0xa3d70a3d70a3d70b, 0x83126e978d4fdf3c,
0xd1b71758e219652e, 0xa7c5ac471b478425, 0x8637bd05af6c69b7,
0xd6bf94d5e57a42be, 0xabcc77118461ceff, 0x89705f4136b4a599,
0xdbe6fecebdedd5c2, 0xafebff0bcb24ab02, 0x8cbccc096f5088cf,
0xe12e13424bb40e18, 0xb424dc35095cd813, 0x901d7cf73ab0acdc,
};
/// <summary>
/// Exponents for both powers of 10^16 and 0.1^16
/// </summary>
private static readonly int[] MultExp64Power10By16 = new int[]
{
54, 107, 160, 213, 266, 319, 373, 426, 479, 532, 585, 638,
691, 745, 798, 851, 904, 957, 1010, 1064, 1117,
};
/// <summary>
/// Normalized powers of 10^16
/// </summary>
private static readonly ulong[] MultVal64Power10By16 = new ulong[]
{
0x8e1bc9bf04000000, 0x9dc5ada82b70b59e, 0xaf298d050e4395d6,
0xc2781f49ffcfa6d4, 0xd7e77a8f87daf7fa, 0xefb3ab16c59b14a0,
0x850fadc09923329c, 0x93ba47c980e98cde, 0xa402b9c5a8d3a6e6,
0xb616a12b7fe617a8, 0xca28a291859bbf90, 0xe070f78d39275566,
0xf92e0c3537826140, 0x8a5296ffe33cc92c, 0x9991a6f3d6bf1762,
0xaa7eebfb9df9de8a, 0xbd49d14aa79dbc7e, 0xd226fc195c6a2f88,
0xe950df20247c83f8, 0x81842f29f2cce373, 0x8fcac257558ee4e2,
};
/// <summary>
/// Normalized powers of 0.1^16
/// </summary>
private static readonly ulong[] MultVal64Power10By16Inv = new ulong[]
{
0xe69594bec44de160, 0xcfb11ead453994c3, 0xbb127c53b17ec165,
0xa87fea27a539e9b3, 0x97c560ba6b0919b5, 0x88b402f7fd7553ab,
0xf64335bcf065d3a0, 0xddd0467c64bce4c4, 0xc7caba6e7c5382ed,
0xb3f4e093db73a0b7, 0xa21727db38cb0053, 0x91ff83775423cc29,
0x8380dea93da4bc82, 0xece53cec4a314f00, 0xd5605fcdcf32e217,
0xc0314325637a1978, 0xad1c8eab5ee43ba2, 0x9becce62836ac5b0,
0x8c71dcd9ba0b495c, 0xfd00b89747823938, 0xe3e27a444d8d991a,
};
/// <summary>
/// Packs <paramref name="val"/>*10^<paramref name="scale"/> as 64-bit floating point value according to IEEE 754 standard
/// </summary>
/// <param name="negative">Sign</param>
/// <param name="val">Mantissa</param>
/// <param name="scale">Exponent</param>
/// <remarks>
/// Adoption of native function NumberToDouble() from coreclr sources,
/// see https://github.com/dotnet/coreclr/blob/master/src/classlibnative/bcltype/number.cpp#L451
/// </remarks>
public static double PackDouble(bool negative, ulong val, int scale)
{
// handle zero value
if (val == 0)
{
return negative ? -0.0 : 0.0;
}
// normalize the mantissa
int exp = 64;
if ((val & 0xFFFFFFFF00000000) == 0) { val <<= 32; exp -= 32; }
if ((val & 0xFFFF000000000000) == 0) { val <<= 16; exp -= 16; }
if ((val & 0xFF00000000000000) == 0) { val <<= 8; exp -= 8; }
if ((val & 0xF000000000000000) == 0) { val <<= 4; exp -= 4; }
if ((val & 0xC000000000000000) == 0) { val <<= 2; exp -= 2; }
if ((val & 0x8000000000000000) == 0) { val <<= 1; exp -= 1; }
if (scale < 0)
{
scale = -scale;
// check scale bounds
if (scale >= 22 * 16)
{
// underflow
return negative ? -0.0 : 0.0;
}
// perform scaling
var index = scale & 15;
if (index != 0)
{
exp -= MultExp64Power10[index - 1] - 1;
val = Mul64Lossy(val, MultVal64Power10Inv[index - 1], ref exp);
}
index = scale >> 4;
if (index != 0)
{
exp -= MultExp64Power10By16[index - 1] - 1;
val = Mul64Lossy(val, MultVal64Power10By16Inv[index - 1], ref exp);
}
}
else
{
// check scale bounds
if (scale >= 22 * 16)
{
// overflow
return negative ? double.NegativeInfinity : double.PositiveInfinity;
}
// perform scaling
var index = scale & 15;
if (index != 0)
{
exp += MultExp64Power10[index - 1];
val = Mul64Lossy(val, MultVal64Power10[index - 1], ref exp);
}
index = scale >> 4;
if (index != 0)
{
exp += MultExp64Power10By16[index - 1];
val = Mul64Lossy(val, MultVal64Power10By16[index - 1], ref exp);
}
}
// round & scale down
if ((val & (1 << 10)) != 0)
{
// IEEE round to even
ulong tmp = val + ((1UL << 10) - 1 + ((val >> 11) & 1));
if (tmp < val)
{
// overflow
tmp = (tmp >> 1) | 0x8000000000000000;
exp++;
}
val = tmp;
}
// return the exponent to a biased state
exp += 0x3FE;
// handle overflow, underflow, "Epsilon - 1/2 Epsilon", denormalized, and the normal case
if (exp <= 0)
{
if (exp == -52 && (val >= 0x8000000000000058))
{
// round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to zero)
val = 0x0000000000000001;
}
else if (exp <= -52)
{
// underflow
val = 0;
}
else
{
// denormalized value
val >>= (-exp + 12);
}
}
else if (exp >= 0x7FF)
{
// overflow
val = 0x7FF0000000000000;
}
else
{
// normal postive exponent case
val = ((ulong)exp << 52) | ((val >> 11) & 0x000FFFFFFFFFFFFF);
}
// apply sign
if (negative)
{
val |= 0x8000000000000000;
}
return BitConverter.Int64BitsToDouble((long)val);
}
private static ulong Mul64Lossy(ulong a, ulong b, ref int exp)
{
ulong a_hi = (a >> 32); uint a_lo = (uint)a;
ulong b_hi = (b >> 32); uint b_lo = (uint)b;
var result = a_hi * b_hi;
// save some multiplications if lo-parts aren't big enough to produce carry
// (hi-parts will be always big enough, since a and b are normalized)
if ((b_lo & 0xFFFF0000) != 0)
result += (a_hi * b_lo) >> 32;
if ((a_lo & 0xFFFF0000) != 0)
result += (a_lo * b_hi) >> 32;
// normalize
if ((result & 0x8000000000000000) == 0) { result <<= 1; exp--; }
return result;
}
}
public static ParseResult DoubleTryParse(char[] chars, int start, int length, out double value)
{
@@ -1092,47 +1294,9 @@ namespace Newtonsoft.Json.Utilities
// correct the decimal point
exponent -= (numDecimalEnd - numDecimalStart);
if (exponent > MaxExponent)
{
return ParseResult.Overflow;
}
value = mantissa;
if (exponent > 0)
{
value *= DoubleExponents[exponent - 1];
}
else if (exponent < 0)
{
exponent = -exponent;
if (exponent > MaxExponent)
{
// handle very small numbers, e.g. 4.94065645841247E-324
exponent -= 154;
if (DoubleNegativeExponents.Length <= exponent - 1)
{
value = 0D;
return ParseResult.Success;
}
value *= DoubleNegativeExponents[154 - 1];
}
value = exponent < 23 ? value / DoubleExponents[exponent - 1] : value * DoubleNegativeExponents[exponent - 1];
}
if (double.IsPositiveInfinity(value))
{
return ParseResult.Overflow;
}
if (isNegative)
{
value = -value;
}
return ParseResult.Success;
value = IEEE754.PackDouble(isNegative, mantissa, exponent);
return double.IsInfinity(value) ? ParseResult.Overflow : ParseResult.Success;
}
public static bool TryConvertGuid(string s, out Guid g)