// The MIT License(MIT) // // Copyright(c) Unity Technologies, Microsoft Corporation // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include "il2cpp-config.h" #include "Number.h" #include "utils/StringUtils.h" #include #define NUMBER_MAXDIGITS 50 struct NUMBER { int precision; int scale; int sign; Il2CppChar digits[NUMBER_MAXDIGITS + 1]; Il2CppChar* allDigits; NUMBER() : precision(0), scale(0), sign(0), allDigits(NULL) { } }; struct FPDOUBLE { #if IL2CPP_BYTE_ORDER == IL2CPP_BIG_ENDIAN unsigned int sign : 1; unsigned int exp : 11; unsigned int mantHi : 20; unsigned int mantLo; #else unsigned int mantLo; unsigned int mantHi : 20; unsigned int exp : 11; unsigned int sign : 1; #endif }; // // 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_t rgval64Power10[] = { // powers of 10 /*1*/ 0xa000000000000000ULL, /*2*/ 0xc800000000000000ULL, /*3*/ 0xfa00000000000000ULL, /*4*/ 0x9c40000000000000ULL, /*5*/ 0xc350000000000000ULL, /*6*/ 0xf424000000000000ULL, /*7*/ 0x9896800000000000ULL, /*8*/ 0xbebc200000000000ULL, /*9*/ 0xee6b280000000000ULL, /*10*/ 0x9502f90000000000ULL, /*11*/ 0xba43b74000000000ULL, /*12*/ 0xe8d4a51000000000ULL, /*13*/ 0x9184e72a00000000ULL, /*14*/ 0xb5e620f480000000ULL, /*15*/ 0xe35fa931a0000000ULL, // powers of 0.1 /*1*/ 0xcccccccccccccccdULL, /*2*/ 0xa3d70a3d70a3d70bULL, /*3*/ 0x83126e978d4fdf3cULL, /*4*/ 0xd1b71758e219652eULL, /*5*/ 0xa7c5ac471b478425ULL, /*6*/ 0x8637bd05af6c69b7ULL, /*7*/ 0xd6bf94d5e57a42beULL, /*8*/ 0xabcc77118461ceffULL, /*9*/ 0x89705f4136b4a599ULL, /*10*/ 0xdbe6fecebdedd5c2ULL, /*11*/ 0xafebff0bcb24ab02ULL, /*12*/ 0x8cbccc096f5088cfULL, /*13*/ 0xe12e13424bb40e18ULL, /*14*/ 0xb424dc35095cd813ULL, /*15*/ 0x901d7cf73ab0acdcULL, }; static const int8_t 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_t rgval64Power10By16[] = { // powers of 10^16 /*1*/ 0x8e1bc9bf04000000ULL, /*2*/ 0x9dc5ada82b70b59eULL, /*3*/ 0xaf298d050e4395d6ULL, /*4*/ 0xc2781f49ffcfa6d4ULL, /*5*/ 0xd7e77a8f87daf7faULL, /*6*/ 0xefb3ab16c59b14a0ULL, /*7*/ 0x850fadc09923329cULL, /*8*/ 0x93ba47c980e98cdeULL, /*9*/ 0xa402b9c5a8d3a6e6ULL, /*10*/ 0xb616a12b7fe617a8ULL, /*11*/ 0xca28a291859bbf90ULL, /*12*/ 0xe070f78d39275566ULL, /*13*/ 0xf92e0c3537826140ULL, /*14*/ 0x8a5296ffe33cc92cULL, /*15*/ 0x9991a6f3d6bf1762ULL, /*16*/ 0xaa7eebfb9df9de8aULL, /*17*/ 0xbd49d14aa79dbc7eULL, /*18*/ 0xd226fc195c6a2f88ULL, /*19*/ 0xe950df20247c83f8ULL, /*20*/ 0x81842f29f2cce373ULL, /*21*/ 0x8fcac257558ee4e2ULL, // powers of 0.1^16 /*1*/ 0xe69594bec44de160ULL, /*2*/ 0xcfb11ead453994c3ULL, /*3*/ 0xbb127c53b17ec165ULL, /*4*/ 0xa87fea27a539e9b3ULL, /*5*/ 0x97c560ba6b0919b5ULL, /*6*/ 0x88b402f7fd7553abULL, /*7*/ 0xf64335bcf065d3a0ULL, /*8*/ 0xddd0467c64bce4c4ULL, /*9*/ 0xc7caba6e7c5382edULL, /*10*/ 0xb3f4e093db73a0b7ULL, /*11*/ 0xa21727db38cb0053ULL, /*12*/ 0x91ff83775423cc29ULL, /*13*/ 0x8380dea93da4bc82ULL, /*14*/ 0xece53cec4a314f00ULL, /*15*/ 0xd5605fcdcf32e217ULL, /*16*/ 0xc0314325637a1978ULL, /*17*/ 0xad1c8eab5ee43ba2ULL, /*18*/ 0x9becce62836ac5b0ULL, /*19*/ 0x8c71dcd9ba0b495cULL, /*20*/ 0xfd00b89747823938ULL, /*21*/ 0xe3e27a444d8d991aULL, }; static const int16_t 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, }; #define Mul32x32To64(a, b) ((uint64_t)((uint32_t)(a)) * (uint64_t)((uint32_t)(b))) #ifdef _DEBUG // // slower high precision version of Mul64 for computation of the tables // static uint64_t Mul64Precise(uint64_t a, uint64_t b, int* pexp) { uint64_t hilo = ((Mul32x32To64(a >> 32, b) >> 1) + (Mul32x32To64(a, b >> 32) >> 1) + (Mul32x32To64(a, b) >> 33)) >> 30; uint64_t val = Mul32x32To64(a >> 32, b >> 32) + (hilo >> 1) + (hilo & 1); // normalize if ((val & 0x8000000000000000L) == 0) { val <<= 1; *pexp -= 1; } return val; } // // debug-only verification of the precomputed tables // static void CheckTable(uint64_t val, int exp, const void* table, int size, const char* name, int tabletype) { uint64_t multval = val; int mulexp = exp; bool fBad = false; for (int i = 0; i < size; i++) { switch (tabletype) { case 1: if (((uint64_t*)table)[i] != val) { if (!fBad) { fprintf(stderr, "%s:\n", name); fBad = true; } fprintf(stderr, "/*%d*/ I64(0x%llx),\n", i + 1, val); } break; case 2: if (((int8_t*)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_t*)table)[i] != exp) { if (!fBad) { fprintf(stderr, "%s:\n", name); fBad = true; } fprintf(stderr, "/*%d*/ %d,\n", i + 1, exp); } break; default: IL2CPP_ASSERT(false); break; } exp += mulexp; val = Mul64Precise(val, multval, &exp); } IL2CPP_ASSERT(!fBad || !"NumberToDouble table not correct. Correct version dumped to stderr."); } void CheckTables() { uint64_t val; int exp; val = 0xa000000000000000L; exp = 4; // 10 CheckTable(val, exp, rgval64Power10, 15, "rgval64Power10", 1); CheckTable(val, exp, rgexp64Power10, 15, "rgexp64Power10", 2); val = 0x8e1bc9bf04000000L; exp = 54; //10^16 CheckTable(val, exp, rgval64Power10By16, 21, "rgval64Power10By16", 1); CheckTable(val, exp, rgexp64Power10By16, 21, "rgexp64Power10By16", 3); val = 0xCCCCCCCCCCCCCCCDL; exp = -3; // 0.1 CheckTable(val, exp, rgval64Power10 + 15, 15, "rgval64Power10 - inv", 1); val = 0xe69594bec44de160L; exp = -53; // 0.1^16 CheckTable(val, exp, rgval64Power10By16 + 21, 21, "rgval64Power10By16 - inv", 1); } #endif // _DEBUG namespace il2cpp { namespace icalls { namespace mscorlib { namespace System { #define DECIMAL_NEG ((uint8_t)0x80) #define DECIMAL_PRECISION 29 #define DECIMAL_SCALE(dec) ((dec).u.u.scale) #define DECIMAL_SIGN(dec) ((dec).u.u.sign) #define DECIMAL_SIGNSCALE(dec) ((dec).u.signscale) #define DECIMAL_LO32(dec) ((dec).v.v.Lo32) #define DECIMAL_MID32(dec) ((dec).v.v.Mid32) #define DECIMAL_HI32(dec) ((dec).Hi32) static void DecShiftLeft(Il2CppDecimal* value) { unsigned int c0 = DECIMAL_LO32(*value) & 0x80000000 ? 1 : 0; unsigned int c1 = DECIMAL_MID32(*value) & 0x80000000 ? 1 : 0; IL2CPP_ASSERT(value != NULL); DECIMAL_LO32(*value) <<= 1; DECIMAL_MID32(*value) = DECIMAL_MID32(*value) << 1 | c0; DECIMAL_HI32(*value) = DECIMAL_HI32(*value) << 1 | c1; } static int D32AddCarry(uint32_t* value, uint32_t i) { uint32_t v = *value; uint32_t sum = v + i; *value = sum; return sum < v || sum < i ? 1 : 0; } static void DecAdd(Il2CppDecimal *value, Il2CppDecimal* d) { IL2CPP_ASSERT(value != NULL && d != NULL); if (D32AddCarry(&DECIMAL_LO32(*value), DECIMAL_LO32(*d))) { if (D32AddCarry(&DECIMAL_MID32(*value), 1)) { D32AddCarry(&DECIMAL_HI32(*value), 1); } } if (D32AddCarry(&DECIMAL_MID32(*value), DECIMAL_MID32(*d))) { D32AddCarry(&DECIMAL_HI32(*value), 1); } D32AddCarry(&DECIMAL_HI32(*value), DECIMAL_HI32(*d)); } static void DecMul10(Il2CppDecimal* value) { Il2CppDecimal d = *value; IL2CPP_ASSERT(value != NULL); DecShiftLeft(value); DecShiftLeft(value); DecAdd(value, &d); DecShiftLeft(value); } static void DecAddInt32(Il2CppDecimal* value, unsigned int i) { IL2CPP_ASSERT(value != NULL); if (D32AddCarry(&DECIMAL_LO32(*value), i)) { if (D32AddCarry(&DECIMAL_MID32(*value), 1)) { D32AddCarry(&DECIMAL_HI32(*value), 1); } } } bool Number::NumberBufferToDecimal(uint8_t* number, Il2CppDecimal* value) { IL2CPP_ASSERT(number != NULL); IL2CPP_ASSERT(value != NULL); NUMBER* numberStruct = (NUMBER*)number; Il2CppChar* p = numberStruct->digits; Il2CppDecimal d; int e = numberStruct->scale; d.reserved = 0; DECIMAL_SIGNSCALE(d) = 0; DECIMAL_HI32(d) = 0; DECIMAL_LO32(d) = 0; DECIMAL_MID32(d) = 0; IL2CPP_ASSERT(p != NULL); if (!*p) { // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force // the scale to 0 if the scale was previously positive if (e > 0) { e = 0; } } else { if (e > DECIMAL_PRECISION) return 0; while ((e > 0 || (*p && e > -28)) && (DECIMAL_HI32(d) < 0x19999999 || (DECIMAL_HI32(d) == 0x19999999 && (DECIMAL_MID32(d) < 0x99999999 || (DECIMAL_MID32(d) == 0x99999999 && (DECIMAL_LO32(d) < 0x99999999 || (DECIMAL_LO32(d) == 0x99999999 && *p <= '5'))))))) { DecMul10(&d); if (*p) DecAddInt32(&d, *p++ - '0'); e--; } if (*p++ >= '5') { bool round = true; if (*(p - 1) == '5' && *(p - 2) % 2 == 0) { // Check if previous digit is even, only if the when we are unsure whether hows to do Banker's rounding // For digits > 5 we will be roundinp up anyway. int count = 20; // Look at the next 20 digits to check to round while (*p == '0' && count != 0) { p++; count--; } if (*p == '\0' || count == 0) round = false; // Do nothing } if (round) { DecAddInt32(&d, 1); if ((DECIMAL_HI32(d) | DECIMAL_MID32(d) | DECIMAL_LO32(d)) == 0) { DECIMAL_HI32(d) = 0x19999999; DECIMAL_MID32(d) = 0x99999999; DECIMAL_LO32(d) = 0x9999999A; e++; } } } } if (e > 0) return 0; if (e <= -DECIMAL_PRECISION) { // Parsing a large scale zero can give you more precision than fits in the decimal. // This should only happen for actual zeros or very small numbers that round to zero. DECIMAL_SIGNSCALE(d) = 0; DECIMAL_HI32(d) = 0; DECIMAL_LO32(d) = 0; DECIMAL_MID32(d) = 0; DECIMAL_SCALE(d) = (DECIMAL_PRECISION - 1); } else { DECIMAL_SCALE(d) = (uint8_t)(-e); } DECIMAL_SIGN(d) = numberStruct->sign ? DECIMAL_NEG : 0; *value = d; return 1; } // // get 32-bit integer from at most 9 digits // static inline unsigned DigitsToInt(Il2CppChar* p, int count) { IL2CPP_ASSERT(1 <= count && count <= 9); Il2CppChar* end = p + count; unsigned res = *p - '0'; for (p = p + 1; p < end; p++) res = 10 * res + *p - '0'; return res; } static uint64_t Mul64Lossy(uint64_t a, uint64_t b, int* pexp) { // 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_t val = Mul32x32To64(a >> 32, b >> 32) + (Mul32x32To64(a >> 32, b) >> 32) + (Mul32x32To64(a, b >> 32) >> 32); // normalize if ((val & 0x8000000000000000ULL) == 0) { val <<= 1; *pexp -= 1; } return val; } static inline void NumberToDouble(NUMBER* number, double* value) { uint64_t val; int exp; Il2CppChar* 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)utils::StringUtils::StrLen(src); remaining = total; // skip the leading zeros while (*src == '0') { remaining--; src++; } if (remaining == 0) { *value = 0; goto done; } count = std::min(remaining, 9); remaining -= count; val = DigitsToInt(src, count); if (remaining > 0) { count = std::min(remaining, 9); remaining -= count; // get the denormalized power of 10 uint32_t mult = (uint32_t)(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_t*)value = (scale > 0) ? 0x7FF0000000000000ULL : 0; goto done; } exp = 64; // normalize the mantissa if ((val & 0xFFFFFFFF00000000ULL) == 0) { val <<= 32; exp -= 32; } if ((val & 0xFFFF000000000000ULL) == 0) { val <<= 16; exp -= 16; } if ((val & 0xFF00000000000000ULL) == 0) { val <<= 8; exp -= 8; } if ((val & 0xF000000000000000ULL) == 0) { val <<= 4; exp -= 4; } if ((val & 0xC000000000000000ULL) == 0) { val <<= 2; exp -= 2; } if ((val & 0x8000000000000000ULL) == 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_t 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_t multval = rgval64Power10By16[index + ((scale < 0) ? 21 : 0) - 1]; val = Mul64Lossy(val, multval, &exp); } // round & scale down if ((uint32_t)val & (1 << 10)) { // IEEE round to even uint64_t tmp = val + ((1 << 10) - 1) + (((uint32_t)val >> 11) & 1); if (tmp < val) { // overflow tmp = (tmp >> 1) | 0x8000000000000000ULL; 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 >= 0x8000000000000058ULL)) { // round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to zero) val = 0x0000000000000001ULL; } else if (exp <= -52) { // underflow val = 0; } else { // denormalized val >>= (-exp + 11 + 1); } } else if (exp >= 0x7FF) { // overflow val = 0x7FF0000000000000ULL; } else { // normal postive exponent case val = ((uint64_t)exp << 52) + ((val >> 11) & 0x000FFFFFFFFFFFFFULL); } *(uint64_t*)value = val; done: if (number->sign) *(uint64_t*)value |= 0x8000000000000000ULL; } bool Number::NumberBufferToDouble(uint8_t* number, double* value) { double d = 0; NumberToDouble((NUMBER*)number, &d); unsigned int e = ((FPDOUBLE*)&d)->exp; unsigned int fmntLow = ((FPDOUBLE*)&d)->mantLo; unsigned int fmntHigh = ((FPDOUBLE*)&d)->mantHi; if (e == 0x7FF) return false; if (e == 0 && fmntLow == 0 && fmntHigh == 0) d = 0; *value = d; return true; } } // namespace System } // namespace mscorlib } // namespace icalls } // namespace il2cpp