From 09cc49e8cac72915b72240c766e25ada171e9fe7 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sat, 22 Sep 2018 06:43:30 -0700 Subject: [PATCH] Porting NumberToDouble to managed code. (#20080) * Porting NumberToDouble to managed code. * Deleting bcltype/number.cpp and bcltype/number.h * Fixing NumberToDouble to call Int64BitsToDouble, rather than DoubleToInt64Bits * Some minor code cleanup in NumberToDouble for better readability. * Some additional code cleanup in the Number.NumberToDouble.cs code --- .../System.Private.CoreLib.csproj | 1 - .../shared/System.Private.CoreLib.Shared.projitems | 1 + .../shared/System/Number.NumberBuffer.cs | 7 +- .../shared/System/Number.NumberToDouble.cs | 449 +++++++++++++++++++++ .../src/System/Number.CoreCLR.cs | 14 - src/classlibnative/bcltype/CMakeLists.txt | 1 - src/classlibnative/bcltype/number.cpp | 441 -------------------- src/classlibnative/bcltype/number.h | 47 --- src/vm/ecalllist.h | 5 - src/vm/mscorlib.cpp | 1 - 10 files changed, 453 insertions(+), 514 deletions(-) create mode 100644 src/System.Private.CoreLib/shared/System/Number.NumberToDouble.cs delete mode 100644 src/System.Private.CoreLib/src/System/Number.CoreCLR.cs delete mode 100644 src/classlibnative/bcltype/number.cpp delete mode 100644 src/classlibnative/bcltype/number.h diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj index a6abde3..f7064c8 100644 --- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -276,7 +276,6 @@ - diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index af7493c..378fa08 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -283,6 +283,7 @@ + diff --git a/src/System.Private.CoreLib/shared/System/Number.NumberBuffer.cs b/src/System.Private.CoreLib/shared/System/Number.NumberBuffer.cs index fe9eaf6..c4d5ba5 100644 --- a/src/System.Private.CoreLib/shared/System/Number.NumberBuffer.cs +++ b/src/System.Private.CoreLib/shared/System/Number.NumberBuffer.cs @@ -3,14 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; using Internal.Runtime.CompilerServices; namespace System { internal static partial class Number { - private const int NumberMaxDigits = 50; // needs to == NUMBER_MAXDIGITS in coreclr's src/classlibnative/bcltype/number.h. + private const int NumberMaxDigits = 50; private const double Log10V2 = 0.30102999566398119521373889472449; @@ -18,7 +17,7 @@ namespace System private const double DriftFactor = 0.69; [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal unsafe ref struct NumberBuffer // needs to match layout of NUMBER in coreclr's src/classlibnative/bcltype/number.h + internal unsafe ref struct NumberBuffer { public int precision; // 0 public int scale; // 4 @@ -35,7 +34,7 @@ namespace System private struct DigitsAndNullTerminator { } } - internal enum NumberBufferKind // needs to match NUMBER_KIND in coreclr's src/classlibnative/bcltype/number.h + internal enum NumberBufferKind { Unknown = 0, Integer = 1, diff --git a/src/System.Private.CoreLib/shared/System/Number.NumberToDouble.cs b/src/System.Private.CoreLib/shared/System/Number.NumberToDouble.cs new file mode 100644 index 0000000..4e4c5bc --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Number.NumberToDouble.cs @@ -0,0 +1,449 @@ +// 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. + +using System.Diagnostics; + +namespace System +{ + internal unsafe partial class Number + { + // precomputed tables with powers of 10. These allows us to do at most + // two Mul64 during the conversion. This is important not only + // for speed, but also for precision because of Mul64 computes with 1 bit error. + + private static readonly ulong[] s_Pow10MantissaTable = new ulong[] + { + // powers of 10 + 0XA0000000_00000000, // 1 + 0XC8000000_00000000, // 2 + 0XFA000000_00000000, // 3 + 0X9C400000_00000000, // 4 + 0XC3500000_00000000, // 5 + 0XF4240000_00000000, // 6 + 0X98968000_00000000, // 7 + 0XBEBC2000_00000000, // 8 + 0XEE6B2800_00000000, // 9 + 0X9502F900_00000000, // 10 + 0XBA43B740_00000000, // 11 + 0XE8D4A510_00000000, // 12 + 0X9184E72A_00000000, // 13 + 0XB5E620F4_80000000, // 14 + 0XE35FA931_A0000000, // 15 + + // powers of 0.1 + 0xCCCCCCCC_CCCCCCCD, // 1 + 0xA3D70A3D_70A3D70B, // 2 + 0x83126E97_8D4FDF3C, // 3 + 0xD1B71758_E219652E, // 4 + 0xA7C5AC47_1B478425, // 5 + 0x8637BD05_AF6C69B7, // 6 + 0xD6BF94D5_E57A42BE, // 7 + 0xABCC7711_8461CEFF, // 8 + 0x89705F41_36B4A599, // 9 + 0xDBE6FECE_BDEDD5C2, // 10 + 0xAFEBFF0B_CB24AB02, // 11 + 0x8CBCCC09_6F5088CF, // 12 + 0xE12E1342_4BB40E18, // 13 + 0xB424DC35_095CD813, // 14 + 0x901D7CF7_3AB0ACDC, // 15 + }; + + private static readonly short[] s_Pow10ExponentTable = new short[] + { + // exponents for both powers of 10 and 0.1 + 4, // 1 + 7, // 2 + 10, // 3 + 14, // 4 + 17, // 5 + 20, // 6 + 24, // 7 + 27, // 8 + 30, // 9 + 34, // 10 + 37, // 11 + 40, // 12 + 44, // 13 + 47, // 14 + 50, // 15 + }; + + private static readonly ulong[] s_Pow10By16MantissaTable = new ulong[] + { + // powers of 10^16 + 0x8E1BC9BF_04000000, // 1 + 0x9DC5ADA8_2B70B59E, // 2 + 0xAF298D05_0E4395D6, // 3 + 0xC2781F49_FFCFA6D4, // 4 + 0xD7E77A8F_87DAF7FA, // 5 + 0xEFB3AB16_C59B14A0, // 6 + 0x850FADC0_9923329C, // 7 + 0x93BA47C9_80E98CDE, // 8 + 0xA402B9C5_A8D3A6E6, // 9 + 0xB616A12B_7FE617A8, // 10 + 0xCA28A291_859BBF90, // 11 + 0xE070F78D_39275566, // 12 + 0xF92E0C35_37826140, // 13 + 0x8A5296FF_E33CC92C, // 14 + 0x9991A6F3_D6BF1762, // 15 + 0xAA7EEBFB_9DF9DE8A, // 16 + 0xBD49D14A_A79DBC7E, // 17 + 0xD226FC19_5C6A2F88, // 18 + 0xE950DF20_247C83F8, // 19 + 0x81842F29_F2CCE373, // 20 + 0x8FCAC257_558EE4E2, // 21 + + // powers of 0.1^16 + 0xE69594BE_C44DE160, // 1 + 0xCFB11EAD_453994C3, // 2 + 0xBB127C53_B17EC165, // 3 + 0xA87FEA27_A539E9B3, // 4 + 0x97C560BA_6B0919B5, // 5 + 0x88B402F7_FD7553AB, // 6 + 0xF64335BC_F065D3A0, // 7 + 0xDDD0467C_64BCE4C4, // 8 + 0xC7CABA6E_7C5382ED, // 9 + 0xB3F4E093_DB73A0B7, // 10 + 0xA21727DB_38CB0053, // 11 + 0x91FF8377_5423CC29, // 12 + 0x8380DEA9_3DA4BC82, // 13 + 0xECE53CEC_4A314F00, // 14 + 0xD5605FCD_CF32E217, // 15 + 0xC0314325_637A1978, // 16 + 0xAD1C8EAB_5EE43BA2, // 17 + 0x9BECCE62_836AC5B0, // 18 + 0x8C71DCD9_BA0B495C, // 19 + 0xFD00B897_47823938, // 20 + 0xE3E27A44_4D8D991A, // 21 + }; + + private static readonly short[] s_Pow10By16ExponentTable = new short[] + { + // exponents for both powers of 10^16 and 0.1^16 + 54, // 1 + 107, // 2 + 160, // 3 + 213, // 4 + 266, // 5 + 319, // 6 + 373, // 7 + 426, // 8 + 479, // 9 + 532, // 10 + 585, // 11 + 638, // 12 + 691, // 13 + 745, // 14 + 798, // 15 + 851, // 16 + 904, // 17 + 957, // 18 + 1010, // 19 + 1064, // 20 + 1117, // 21 + }; + +#if DEBUG + private static bool s_CheckedTables = false; + + private static void CheckTables() + { + ulong val; + int exp; + + val = 0xA0000000_00000000; + exp = 4; // 10 + + CheckPow10MantissaTable(val, exp, new Span(s_Pow10MantissaTable, 0, 15), nameof(s_Pow10MantissaTable)); + CheckPow10ExponentTable(val, exp, new Span(s_Pow10ExponentTable, 0, 15), nameof(s_Pow10ExponentTable)); + + val = 0x8E1BC9BF_04000000; + exp = 54; //10^16 + + CheckPow10MantissaTable(val, exp, new Span(s_Pow10By16MantissaTable, 0, 21), nameof(s_Pow10By16MantissaTable)); + CheckPow10ExponentTable(val, exp, new Span(s_Pow10By16ExponentTable, 0, 21), nameof(s_Pow10By16ExponentTable)); + + val = 0xCCCCCCCC_CCCCCCCD; + exp = -3; // 0.1 + + CheckPow10MantissaTable(val, exp, new Span(s_Pow10MantissaTable, 15, 15), nameof(s_Pow10MantissaTable) + " - inv"); + + val = 0xE69594BE_C44DE160; + exp = -53; // 0.1^16 + + CheckPow10MantissaTable(val, exp, new Span(s_Pow10By16MantissaTable, 21, 21), nameof(s_Pow10By16MantissaTable) + " - inv"); + } + + // debug-only verification of the precomputed tables + private static void CheckPow10MantissaTable(ulong val, int exp, Span table, string name) + { + ulong multval = val; + int mulexp = exp; + bool isBad = false; + + for (int i = 0; i < table.Length; i++) + { + if (table[i] != val) + { + if (!isBad) + { + Debug.WriteLine(name); + isBad = true; + } + Debug.WriteLine($"0x{val:X16}, // {i + 1}"); + } + + exp += mulexp; + val = Mul64Precise(val, multval, ref exp); + } + + Debug.Assert(!isBad, "NumberToDouble table not correct. Correct version dumped to debug output."); + } + + // debug-only verification of the precomputed tables + private static void CheckPow10ExponentTable(ulong val, int exp, Span table, string name) + { + ulong multval = val; + int mulexp = exp; + bool isBad = false; + + for (int i = 0; i < table.Length; i++) + { + if (table[i] != exp) + { + if (!isBad) + { + Debug.WriteLine(name); + isBad = true; + } + Debug.WriteLine($"{val}, // {i + 1}"); + } + + exp += mulexp; + val = Mul64Precise(val, multval, ref exp); + } + + Debug.Assert(!isBad, "NumberToDouble table not correct. Correct version dumped to debug output."); + } + + // slower high precision version of Mul64 for computation of the tables + private static ulong Mul64Precise(ulong a, ulong b, ref int exp) + { + ulong hilo = ((Mul32x32To64((uint)(a >> 32), (uint)(b)) >> 1) + + (Mul32x32To64((uint)(a), (uint)(b >> 32)) >> 1) + + (Mul32x32To64((uint)(a), (uint)(b)) >> 33)) >> 30; + + ulong val = Mul32x32To64((uint)(a >> 32), (uint)(b >> 32)) + + (hilo >> 1) + + (hilo & 1); + + // normalize + if ((val & 0x80000000_00000000) == 0) + { + val <<= 1; + exp--; + } + + return val; + } +#endif + + // get 32-bit integer from at most 9 digits + private static uint DigitsToInt(char* p, int count) + { + Debug.Assert((1 <= count) && (count <= 9)); + + char* end = (p + count); + uint res = (uint)(p[0] - '0'); + + for (p++; p < end; p++) + { + res = (10 * res) + p[0] - '0'; + } + + return res; + } + + private static int GetLength(char* src) + { + int length = 0; + + while (src[length] != '\0') + { + length++; + } + + return length; + } + + // helper function to multiply two 32-bit uints + private static ulong Mul32x32To64(uint a, uint b) + { + return (ulong)(a) * b; + } + + // multiply two numbers in the internal integer representation + private static ulong Mul64Lossy(ulong a, ulong b, ref int exp) + { + // it's ok to lose some precision here - Mul64 will be called + // at most twice during the conversion, so the error won't propagate + // to any of the 53 significant bits of the result + ulong val = Mul32x32To64((uint)(a >> 32), (uint)(b >> 32)) + + (Mul32x32To64((uint)(a >> 32), (uint)(b)) >> 32) + + (Mul32x32To64((uint)(a), (uint)(b >> 32)) >> 32); + + // normalize + if ((val & 0x80000000_00000000) == 0) + { + val <<= 1; + exp--; + } + + return val; + } + + private static double NumberToDouble(ref NumberBuffer number) + { +#if DEBUG + + if (!s_CheckedTables) + { + CheckTables(); + s_CheckedTables = true; + } +#endif + + char* src = number.digits; + int total = GetLength(src); + int remaining = total; + + // skip the leading zeros + while (src[0] == '0') + { + remaining--; + src++; + } + + if (remaining == 0) + { + return number.sign ? -0.0 : 0.0; + } + + int count = Math.Min(remaining, 9); + remaining -= count; + ulong val = DigitsToInt(src, count); + + if (remaining > 0) + { + count = Math.Min(remaining, 9); + remaining -= count; + + // get the denormalized power of 10 + uint mult = (uint)(s_Pow10MantissaTable[count - 1] >> (64 - s_Pow10ExponentTable[count - 1])); + val = Mul32x32To64((uint)(val), mult) + DigitsToInt(src + 9, count); + } + + int scale = number.scale - (total - remaining); + int absscale = Math.Abs(scale); + if (absscale >= 22 * 16) + { + // overflow / underflow + if (scale > 0) + { + return number.sign ? double.NegativeInfinity : double.PositiveInfinity; + } + else + { + return number.sign ? -0.0 : 0.0; + } + } + + int exp = 64; + + // normalize the mantissa + if ((val & 0xFFFFFFFF_00000000) == 0) { val <<= 32; exp -= 32; } + if ((val & 0xFFFF0000_00000000) == 0) { val <<= 16; exp -= 16; } + if ((val & 0xFF000000_00000000) == 0) { val <<= 8; exp -= 8; } + if ((val & 0xF0000000_00000000) == 0) { val <<= 4; exp -= 4; } + if ((val & 0xC0000000_00000000) == 0) { val <<= 2; exp -= 2; } + if ((val & 0x80000000_00000000) == 0) { val <<= 1; exp -= 1; } + + int index = absscale & 15; + if (index != 0) + { + int multexp = s_Pow10ExponentTable[index - 1]; + // the exponents are shared between the inverted and regular table + exp += (scale < 0) ? (-multexp + 1) : multexp; + + ulong multval = s_Pow10MantissaTable[index + ((scale < 0) ? 15 : 0) - 1]; + val = Mul64Lossy(val, multval, ref exp); + } + + index = absscale >> 4; + if (index != 0) + { + int multexp = s_Pow10By16ExponentTable[index - 1]; + // the exponents are shared between the inverted and regular table + exp += (scale < 0) ? (-multexp + 1) : multexp; + + ulong multval = s_Pow10By16MantissaTable[index + ((scale < 0) ? 21 : 0) - 1]; + val = Mul64Lossy(val, multval, ref exp); + } + + // round & scale down + if (((uint)(val) & (1 << 10)) != 0) + { + // IEEE round to even + ulong tmp = val + ((1 << 10) - 1) + (((uint)(val) >> 11) & 1); + if (tmp < val) + { + // overflow + tmp = (tmp >> 1) | 0x8000000000000000; + exp += 1; + } + 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 + val >>= (-exp + 11 + 1); + } + } + else if (exp >= 0x7FF) + { + // overflow + val = 0x7FF0000000000000; + } + else + { + // normal postive exponent case + val = ((ulong)(exp) << 52) + ((val >> 11) & 0x000FFFFFFFFFFFFF); + } + + if (number.sign) + { + val |= 0x8000000000000000; + } + + return BitConverter.Int64BitsToDouble((long)(val)); + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Number.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Number.CoreCLR.cs deleted file mode 100644 index 310987a..0000000 --- a/src/System.Private.CoreLib/src/System/Number.CoreCLR.cs +++ /dev/null @@ -1,14 +0,0 @@ -// 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. - -using System.Runtime.CompilerServices; - -namespace System -{ - internal static partial class Number - { - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern double NumberToDouble(ref NumberBuffer number); - } -} diff --git a/src/classlibnative/bcltype/CMakeLists.txt b/src/classlibnative/bcltype/CMakeLists.txt index a5a0e63..2f990ad 100644 --- a/src/classlibnative/bcltype/CMakeLists.txt +++ b/src/classlibnative/bcltype/CMakeLists.txt @@ -7,7 +7,6 @@ endif(PerfCountersSupportedBuild) set(BCLTYPE_SOURCES arraynative.cpp arrayhelpers.cpp - number.cpp oavariant.cpp objectnative.cpp stringnative.cpp diff --git a/src/classlibnative/bcltype/number.cpp b/src/classlibnative/bcltype/number.cpp deleted file mode 100644 index 8fe62eb..0000000 --- a/src/classlibnative/bcltype/number.cpp +++ /dev/null @@ -1,441 +0,0 @@ -// 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. -// -// File: Number.cpp -// - -// - -#include "common.h" -#include "number.h" - -typedef wchar_t wchar; - -/*=========================================================== - Portable NumberToDouble implementation - -------------------------------------- - - - does the conversion with the best possible precision. - - does not use any float arithmetic so it is not sensitive - to differences in precision of floating point calculations - across platforms. - - The internal integer representation of the float number is - UINT64 mantissa + INT exponent. The mantissa is kept normalized - ie with the most significant one being 63-th bit of UINT64. -===========================================================*/ - -// -// get 32-bit integer from at most 9 digits -// -static unsigned DigitsToInt(__in_ecount(count) wchar* p, int count) -{ - LIMITED_METHOD_CONTRACT - - _ASSERTE(1 <= count && count <= 9); - wchar* end = p + count; - unsigned res = *p - '0'; - for ( p = p + 1; p < end; p++) { - res = 10 * res + *p - '0'; - } - return res; -} - -// -// helper macro to multiply two 32-bit uints -// -#define Mul32x32To64(a, b) ((UINT64)((UINT32)(a)) * (UINT64)((UINT32)(b))) - - -// -// multiply two numbers in the internal integer representation -// -static UINT64 Mul64Lossy(UINT64 a, UINT64 b, INT* pexp) -{ - LIMITED_METHOD_CONTRACT - - // it's ok to losse some precision here - Mul64 will be called - // at most twice during the conversion, so the error won't propagate - // to any of the 53 significant bits of the result - UINT64 val = Mul32x32To64(a >> 32, b >> 32) + - (Mul32x32To64(a >> 32, b) >> 32) + - (Mul32x32To64(a, b >> 32) >> 32); - - // normalize - if ((val & I64(0x8000000000000000)) == 0) { val <<= 1; *pexp -= 1; } - - return val; -} - -// -// precomputed tables with powers of 10. These allows us to do at most -// two Mul64 during the conversion. This is important not only -// for speed, but also for precision because of Mul64 computes with 1 bit error. -// - -static const UINT64 rgval64Power10[] = { -// powers of 10 -/*1*/ I64(0xa000000000000000), -/*2*/ I64(0xc800000000000000), -/*3*/ I64(0xfa00000000000000), -/*4*/ I64(0x9c40000000000000), -/*5*/ I64(0xc350000000000000), -/*6*/ I64(0xf424000000000000), -/*7*/ I64(0x9896800000000000), -/*8*/ I64(0xbebc200000000000), -/*9*/ I64(0xee6b280000000000), -/*10*/ I64(0x9502f90000000000), -/*11*/ I64(0xba43b74000000000), -/*12*/ I64(0xe8d4a51000000000), -/*13*/ I64(0x9184e72a00000000), -/*14*/ I64(0xb5e620f480000000), -/*15*/ I64(0xe35fa931a0000000), - -// powers of 0.1 -/*1*/ I64(0xcccccccccccccccd), -/*2*/ I64(0xa3d70a3d70a3d70b), -/*3*/ I64(0x83126e978d4fdf3c), -/*4*/ I64(0xd1b71758e219652e), -/*5*/ I64(0xa7c5ac471b478425), -/*6*/ I64(0x8637bd05af6c69b7), -/*7*/ I64(0xd6bf94d5e57a42be), -/*8*/ I64(0xabcc77118461ceff), -/*9*/ I64(0x89705f4136b4a599), -/*10*/ I64(0xdbe6fecebdedd5c2), -/*11*/ I64(0xafebff0bcb24ab02), -/*12*/ I64(0x8cbccc096f5088cf), -/*13*/ I64(0xe12e13424bb40e18), -/*14*/ I64(0xb424dc35095cd813), -/*15*/ I64(0x901d7cf73ab0acdc), -}; - -static const INT8 rgexp64Power10[] = { -// exponents for both powers of 10 and 0.1 -/*1*/ 4, -/*2*/ 7, -/*3*/ 10, -/*4*/ 14, -/*5*/ 17, -/*6*/ 20, -/*7*/ 24, -/*8*/ 27, -/*9*/ 30, -/*10*/ 34, -/*11*/ 37, -/*12*/ 40, -/*13*/ 44, -/*14*/ 47, -/*15*/ 50, -}; - -static const UINT64 rgval64Power10By16[] = { -// powers of 10^16 -/*1*/ I64(0x8e1bc9bf04000000), -/*2*/ I64(0x9dc5ada82b70b59e), -/*3*/ I64(0xaf298d050e4395d6), -/*4*/ I64(0xc2781f49ffcfa6d4), -/*5*/ I64(0xd7e77a8f87daf7fa), -/*6*/ I64(0xefb3ab16c59b14a0), -/*7*/ I64(0x850fadc09923329c), -/*8*/ I64(0x93ba47c980e98cde), -/*9*/ I64(0xa402b9c5a8d3a6e6), -/*10*/ I64(0xb616a12b7fe617a8), -/*11*/ I64(0xca28a291859bbf90), -/*12*/ I64(0xe070f78d39275566), -/*13*/ I64(0xf92e0c3537826140), -/*14*/ I64(0x8a5296ffe33cc92c), -/*15*/ I64(0x9991a6f3d6bf1762), -/*16*/ I64(0xaa7eebfb9df9de8a), -/*17*/ I64(0xbd49d14aa79dbc7e), -/*18*/ I64(0xd226fc195c6a2f88), -/*19*/ I64(0xe950df20247c83f8), -/*20*/ I64(0x81842f29f2cce373), -/*21*/ I64(0x8fcac257558ee4e2), - -// powers of 0.1^16 -/*1*/ I64(0xe69594bec44de160), -/*2*/ I64(0xcfb11ead453994c3), -/*3*/ I64(0xbb127c53b17ec165), -/*4*/ I64(0xa87fea27a539e9b3), -/*5*/ I64(0x97c560ba6b0919b5), -/*6*/ I64(0x88b402f7fd7553ab), -/*7*/ I64(0xf64335bcf065d3a0), -/*8*/ I64(0xddd0467c64bce4c4), -/*9*/ I64(0xc7caba6e7c5382ed), -/*10*/ I64(0xb3f4e093db73a0b7), -/*11*/ I64(0xa21727db38cb0053), -/*12*/ I64(0x91ff83775423cc29), -/*13*/ I64(0x8380dea93da4bc82), -/*14*/ I64(0xece53cec4a314f00), -/*15*/ I64(0xd5605fcdcf32e217), -/*16*/ I64(0xc0314325637a1978), -/*17*/ I64(0xad1c8eab5ee43ba2), -/*18*/ I64(0x9becce62836ac5b0), -/*19*/ I64(0x8c71dcd9ba0b495c), -/*20*/ I64(0xfd00b89747823938), -/*21*/ I64(0xe3e27a444d8d991a), -}; - -static const INT16 rgexp64Power10By16[] = { -// exponents for both powers of 10^16 and 0.1^16 -/*1*/ 54, -/*2*/ 107, -/*3*/ 160, -/*4*/ 213, -/*5*/ 266, -/*6*/ 319, -/*7*/ 373, -/*8*/ 426, -/*9*/ 479, -/*10*/ 532, -/*11*/ 585, -/*12*/ 638, -/*13*/ 691, -/*14*/ 745, -/*15*/ 798, -/*16*/ 851, -/*17*/ 904, -/*18*/ 957, -/*19*/ 1010, -/*20*/ 1064, -/*21*/ 1117, -}; - -#ifdef _DEBUG -// -// slower high precision version of Mul64 for computation of the tables -// -static UINT64 Mul64Precise(UINT64 a, UINT64 b, INT* pexp) -{ - LIMITED_METHOD_CONTRACT - - UINT64 hilo = - ((Mul32x32To64(a >> 32, b) >> 1) + - (Mul32x32To64(a, b >> 32) >> 1) + - (Mul32x32To64(a, b) >> 33)) >> 30; - - UINT64 val = Mul32x32To64(a >> 32, b >> 32) + (hilo >> 1) + (hilo & 1); - - // normalize - if ((val & I64(0x8000000000000000)) == 0) { val <<= 1; *pexp -= 1; } - - return val; -} - - -// -// debug-only verification of the precomputed tables -// -static void CheckTable(UINT64 val, INT exp, LPCVOID table, int size, LPCSTR name, int tabletype) -{ - WRAPPER_NO_CONTRACT - - UINT64 multval = val; - INT mulexp = exp; - bool fBad = false; - for (int i = 0; i < size; i++) { - switch (tabletype) { - case 1: - if (((UINT64*)table)[i] != val) { - if (!fBad) { - fprintf(stderr, "%s:\n", name); - fBad = true; - } - fprintf(stderr, "/*%d*/ I64(0x%I64x),\n", i+1, val); - } - break; - case 2: - if (((INT8*)table)[i] != exp) { - if (!fBad) { - fprintf(stderr, "%s:\n", name); - fBad = true; - } - fprintf(stderr, "/*%d*/ %d,\n", i+1, exp); - } - break; - case 3: - if (((INT16*)table)[i] != exp) { - if (!fBad) { - fprintf(stderr, "%s:\n", name); - fBad = true; - } - fprintf(stderr, "/*%d*/ %d,\n", i+1, exp); - } - break; - default: - _ASSERTE(false); - break; - } - - exp += mulexp; - val = Mul64Precise(val, multval, &exp); - } - _ASSERTE(!fBad || !"NumberToDouble table not correct. Correct version dumped to stderr."); -} - -void CheckTables() -{ - WRAPPER_NO_CONTRACT - - UINT64 val; INT exp; - - val = I64(0xa000000000000000); exp = 4; // 10 - CheckTable(val, exp, rgval64Power10, 15, "rgval64Power10", 1); - CheckTable(val, exp, rgexp64Power10, 15, "rgexp64Power10", 2); - - val = I64(0x8e1bc9bf04000000); exp = 54; //10^16 - CheckTable(val, exp, rgval64Power10By16, 21, "rgval64Power10By16", 1); - CheckTable(val, exp, rgexp64Power10By16, 21, "rgexp64Power10By16", 3); - - val = I64(0xCCCCCCCCCCCCCCCD); exp = -3; // 0.1 - CheckTable(val, exp, rgval64Power10+15, 15, "rgval64Power10 - inv", 1); - - val = I64(0xe69594bec44de160); exp = -53; // 0.1^16 - CheckTable(val, exp, rgval64Power10By16+21, 21, "rgval64Power10By16 - inv", 1); -} -#endif // _DEBUG - -void NumberToDouble(NUMBER* number, double* value) -{ - WRAPPER_NO_CONTRACT - - UINT64 val; - INT exp; - wchar* src = number->digits; - int remaining; - int total; - int count; - int scale; - int absscale; - int index; - -#ifdef _DEBUG - static bool fCheckedTables = false; - if (!fCheckedTables) { - CheckTables(); - fCheckedTables = true; - } -#endif // _DEBUG - - total = (int)wcslen(src); - remaining = total; - - // skip the leading zeros - while (*src == '0') { - remaining--; - src++; - } - - if (remaining == 0) { - *value = 0; - goto done; - } - - count = min(remaining, 9); - remaining -= count; - val = DigitsToInt(src, count); - - if (remaining > 0) { - count = min(remaining, 9); - remaining -= count; - - // get the denormalized power of 10 - UINT32 mult = (UINT32)(rgval64Power10[count-1] >> (64 - rgexp64Power10[count-1])); - val = Mul32x32To64(val, mult) + DigitsToInt(src+9, count); - } - - scale = number->scale - (total - remaining); - absscale = abs(scale); - if (absscale >= 22 * 16) { - // overflow / underflow - *(UINT64*)value = (scale > 0) ? I64(0x7FF0000000000000) : 0; - goto done; - } - - exp = 64; - - // normalize the mantissa - if ((val & I64(0xFFFFFFFF00000000)) == 0) { val <<= 32; exp -= 32; } - if ((val & I64(0xFFFF000000000000)) == 0) { val <<= 16; exp -= 16; } - if ((val & I64(0xFF00000000000000)) == 0) { val <<= 8; exp -= 8; } - if ((val & I64(0xF000000000000000)) == 0) { val <<= 4; exp -= 4; } - if ((val & I64(0xC000000000000000)) == 0) { val <<= 2; exp -= 2; } - if ((val & I64(0x8000000000000000)) == 0) { val <<= 1; exp -= 1; } - - index = absscale & 15; - if (index) { - INT multexp = rgexp64Power10[index-1]; - // the exponents are shared between the inverted and regular table - exp += (scale < 0) ? (-multexp + 1) : multexp; - - UINT64 multval = rgval64Power10[index + ((scale < 0) ? 15 : 0) - 1]; - val = Mul64Lossy(val, multval, &exp); - } - - index = absscale >> 4; - if (index) { - INT multexp = rgexp64Power10By16[index-1]; - // the exponents are shared between the inverted and regular table - exp += (scale < 0) ? (-multexp + 1) : multexp; - - UINT64 multval = rgval64Power10By16[index + ((scale < 0) ? 21 : 0) - 1]; - val = Mul64Lossy(val, multval, &exp); - } - - - // round & scale down - if ((UINT32)val & (1 << 10)) - { - // IEEE round to even - UINT64 tmp = val + ((1 << 10) - 1) + (((UINT32)val >> 11) & 1); - if (tmp < val) { - // overflow - tmp = (tmp >> 1) | I64(0x8000000000000000); - exp += 1; - } - 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 >= I64(0x8000000000000058))) { - // round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to zero) - val = I64(0x0000000000000001); - } - else if (exp <= -52) { - // underflow - val = 0; - } - else { - // denormalized - val >>= (-exp + 11 + 1); - } - } - else if (exp >= 0x7FF) { - // overflow - val = I64(0x7FF0000000000000); - } - else { - // normal postive exponent case - val = ((UINT64)exp << 52) + ((val >> 11) & I64(0x000FFFFFFFFFFFFF)); - } - - *(UINT64*)value = val; - -done: - if (number->sign) *(UINT64*)value |= I64(0x8000000000000000); -} - -FCIMPL1(double, COMNumber::NumberToDoubleFC, NUMBER* number) -{ - FCALL_CONTRACT; - - double d = 0; - NumberToDouble(number, &d); - return d; -} -FCIMPLEND diff --git a/src/classlibnative/bcltype/number.h b/src/classlibnative/bcltype/number.h deleted file mode 100644 index 2359deb..0000000 --- a/src/classlibnative/bcltype/number.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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. -// -// File: Number.h -// - -// - -#ifndef _NUMBER_H_ -#define _NUMBER_H_ - -#include - -#define NUMBER_MAXDIGITS 50 - -static const double LOG10V2 = 0.30102999566398119521373889472449; - -// DRIFT_FACTOR = 1 - LOG10V2 - epsilon (a small number account for drift of floating point multiplication) -static const double DRIFT_FACTOR = 0.69; - -enum NUMBER_KIND : int { - NUMBER_KIND_Unknown = 0, - NUMBER_KIND_Integer = 1, - NUMBER_KIND_Decimal = 2, - NUMBER_KIND_Double = 3 -}; - -struct NUMBER { - int precision; // 0 - int scale; // 4 - int sign; // 8 - NUMBER_KIND kind; // 12 - wchar_t* allDigits; // 16 - wchar_t digits[NUMBER_MAXDIGITS + 1]; // 20 or 24 - NUMBER() : precision(0), scale(0), sign(0), kind(NUMBER_KIND_Unknown), allDigits(NULL) {} -}; - -class COMNumber -{ -public: - static FCDECL1(double, NumberToDoubleFC, NUMBER* number); -}; - -#include - -#endif // _NUMBER_H_ diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index 7983f7f..cd9638a 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -739,10 +739,6 @@ FCFuncStart(gWaitHandleFuncs) FCFuncElement("SignalAndWaitOne", WaitHandleNative::CorSignalAndWaitOneNative) FCFuncEnd() -FCFuncStart(gNumberFuncs) - FCFuncElement("NumberToDouble", COMNumber::NumberToDoubleFC) -FCFuncEnd() - #ifdef FEATURE_COMINTEROP FCFuncStart(gVariantFuncs) FCFuncElement("SetFieldsObject", COMVariant::SetFieldsObject) @@ -1304,7 +1300,6 @@ FCClassElement("MngdSafeArrayMarshaler", "System.StubHelpers", gMngdSafeArrayMar FCClassElement("ModuleBuilder", "System.Reflection.Emit", gCOMModuleBuilderFuncs) FCClassElement("ModuleHandle", "System", gCOMModuleHandleFuncs) FCClassElement("Monitor", "System.Threading", gMonitorFuncs) -FCClassElement("Number", "System", gNumberFuncs) #ifdef FEATURE_COMINTEROP FCClassElement("OAVariantLib", "Microsoft.Win32", gOAVariantFuncs) #endif diff --git a/src/vm/mscorlib.cpp b/src/vm/mscorlib.cpp index a526ebd..240c127 100644 --- a/src/vm/mscorlib.cpp +++ b/src/vm/mscorlib.cpp @@ -42,7 +42,6 @@ #include "floatdouble.h" #include "floatsingle.h" #include "comdatetime.h" -#include "number.h" #include "compatibilityswitch.h" #include "debugdebugger.h" #include "assemblyname.hpp" -- 2.7.4