* 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
<Compile Include="$(BclSourcesRoot)\System\MathF.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\mda.cs" />
<Compile Include="$(BclSourcesRoot)\System\MissingMemberException.CoreCLR.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Number.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\RtType.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeHandles.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Number.Formatting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Number.Grisu3.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Number.NumberBuffer.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Number.NumberToDouble.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Number.Parsing.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\NullReferenceException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ObjectDisposedException.cs" />
// 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;
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
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,
--- /dev/null
+// 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<ulong>(s_Pow10MantissaTable, 0, 15), nameof(s_Pow10MantissaTable));
+ CheckPow10ExponentTable(val, exp, new Span<short>(s_Pow10ExponentTable, 0, 15), nameof(s_Pow10ExponentTable));
+
+ val = 0x8E1BC9BF_04000000;
+ exp = 54; //10^16
+
+ CheckPow10MantissaTable(val, exp, new Span<ulong>(s_Pow10By16MantissaTable, 0, 21), nameof(s_Pow10By16MantissaTable));
+ CheckPow10ExponentTable(val, exp, new Span<short>(s_Pow10By16ExponentTable, 0, 21), nameof(s_Pow10By16ExponentTable));
+
+ val = 0xCCCCCCCC_CCCCCCCD;
+ exp = -3; // 0.1
+
+ CheckPow10MantissaTable(val, exp, new Span<ulong>(s_Pow10MantissaTable, 15, 15), nameof(s_Pow10MantissaTable) + " - inv");
+
+ val = 0xE69594BE_C44DE160;
+ exp = -53; // 0.1^16
+
+ CheckPow10MantissaTable(val, exp, new Span<ulong>(s_Pow10By16MantissaTable, 21, 21), nameof(s_Pow10By16MantissaTable) + " - inv");
+ }
+
+ // debug-only verification of the precomputed tables
+ private static void CheckPow10MantissaTable(ulong val, int exp, Span<ulong> 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<short> 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));
+ }
+ }
+}
+++ /dev/null
-// 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);
- }
-}
set(BCLTYPE_SOURCES
arraynative.cpp
arrayhelpers.cpp
- number.cpp
oavariant.cpp
objectnative.cpp
stringnative.cpp
+++ /dev/null
-// 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
+++ /dev/null
-// 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 <pshpack1.h>
-
-#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 <poppack.h>
-
-#endif // _NUMBER_H_
FCFuncElement("SignalAndWaitOne", WaitHandleNative::CorSignalAndWaitOneNative)
FCFuncEnd()
-FCFuncStart(gNumberFuncs)
- FCFuncElement("NumberToDouble", COMNumber::NumberToDoubleFC)
-FCFuncEnd()
-
#ifdef FEATURE_COMINTEROP
FCFuncStart(gVariantFuncs)
FCFuncElement("SetFieldsObject", COMVariant::SetFieldsObject)
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
#include "floatdouble.h"
#include "floatsingle.h"
#include "comdatetime.h"
-#include "number.h"
#include "compatibilityswitch.h"
#include "debugdebugger.h"
#include "assemblyname.hpp"