diff options
author | Tanner Gooding <tagoo@outlook.com> | 2018-09-22 06:43:30 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-22 06:43:30 -0700 |
commit | 09cc49e8cac72915b72240c766e25ada171e9fe7 (patch) | |
tree | 68cabb0574b24b228ee17fde5eb4f952bde9e76e /src/classlibnative | |
parent | 99bf5ce15efc1179b47d1e1d1522673a30ae8628 (diff) | |
download | coreclr-09cc49e8cac72915b72240c766e25ada171e9fe7.tar.gz coreclr-09cc49e8cac72915b72240c766e25ada171e9fe7.tar.bz2 coreclr-09cc49e8cac72915b72240c766e25ada171e9fe7.zip |
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
Diffstat (limited to 'src/classlibnative')
-rw-r--r-- | src/classlibnative/bcltype/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/classlibnative/bcltype/number.cpp | 441 | ||||
-rw-r--r-- | src/classlibnative/bcltype/number.h | 47 |
3 files changed, 0 insertions, 489 deletions
diff --git a/src/classlibnative/bcltype/CMakeLists.txt b/src/classlibnative/bcltype/CMakeLists.txt index a5a0e63a81..2f990ad9a0 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 8fe62eb414..0000000000 --- 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 2359deb9ad..0000000000 --- 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 <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_ |