summaryrefslogtreecommitdiff
path: root/src/classlibnative/bcltype/number.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/classlibnative/bcltype/number.cpp')
-rw-r--r--src/classlibnative/bcltype/number.cpp3005
1 files changed, 3005 insertions, 0 deletions
diff --git a/src/classlibnative/bcltype/number.cpp b/src/classlibnative/bcltype/number.cpp
new file mode 100644
index 0000000000..349bffb819
--- /dev/null
+++ b/src/classlibnative/bcltype/number.cpp
@@ -0,0 +1,3005 @@
+// 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 "excep.h"
+#include "number.h"
+#include "string.h"
+#include "decimal.h"
+#include <stdlib.h>
+
+typedef wchar_t wchar;
+
+#define INT32_PRECISION 10
+#define UINT32_PRECISION INT32_PRECISION
+#define INT64_PRECISION 19
+#define UINT64_PRECISION 20
+#define FLOAT_PRECISION 7
+#define DOUBLE_PRECISION 15
+#define LARGE_BUFFER_SIZE 600
+#define MIN_BUFFER_SIZE 105
+
+#define SCALE_NAN 0x80000000
+#define SCALE_INF 0x7FFFFFFF
+
+struct FPSINGLE {
+#if BIGENDIAN
+ unsigned int sign: 1;
+ unsigned int exp: 8;
+ unsigned int mant: 23;
+#else
+ unsigned int mant: 23;
+ unsigned int exp: 8;
+ unsigned int sign: 1;
+#endif
+};
+
+struct FPDOUBLE {
+#if BIGENDIAN
+ 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
+};
+
+
+static const char* const posCurrencyFormats[] = {
+ "$#", "#$", "$ #", "# $"};
+
+static const char* const negCurrencyFormats[] = {
+ "($#)", "-$#", "$-#", "$#-",
+ "(#$)", "-#$", "#-$", "#$-",
+ "-# $", "-$ #", "# $-", "$ #-",
+ "$ -#", "#- $", "($ #)", "(# $)"};
+
+static const char* const posPercentFormats[] = {
+ "# %", "#%", "%#", "% #" // Last one is new in Whidbey
+};
+
+static const char* const negPercentFormats[] = {
+ "-# %", "-#%", "-%#",
+ "%-#", "%#-", // Last 9 are new in WHidbey
+ "#-%", "#%-",
+ "-% #", "# %-", "% #-",
+ "% -#", "#- %"
+};
+
+static const char* const negNumberFormats[] = {
+ "(#)", "-#", "- #", "#-", "# -",
+};
+
+static const char posNumberFormat[] = "#";
+
+#if defined(_TARGET_X86_)
+
+extern "C" void _cdecl /*__stdcall*/ DoubleToNumber(double value, int precision, NUMBER* number);
+extern "C" void _cdecl /*__stdcall*/ NumberToDouble(NUMBER* number, double* value);
+
+#pragma warning(disable:4035)
+
+wchar_t* COMNumber::Int32ToDecChars(__in wchar_t* p, unsigned int value, int digits)
+{
+ LIMITED_METHOD_CONTRACT
+
+ _asm {
+ mov eax,value
+ mov ebx,10
+ mov ecx,digits
+ mov edi,p
+ jmp L2
+L1: xor edx,edx
+ div ebx
+ add edx,'0' //promote dl to edx to avoid partial register stall and LCP stall
+ sub edi,2
+ mov [edi],dx
+L2: dec ecx
+ jge L1
+ or eax,eax
+ jne L1
+ mov eax,edi
+ }
+}
+
+unsigned int Int64DivMod1E9(unsigned __int64* value)
+{
+ LIMITED_METHOD_CONTRACT
+
+ _asm {
+ mov ebx,value
+ mov ecx,1000000000
+ xor edx,edx
+ mov eax,[ebx+4]
+ div ecx
+ mov [ebx+4],eax
+ mov eax,[ebx]
+ div ecx
+ mov [ebx],eax
+ mov eax,edx
+ }
+}
+
+#pragma warning(default:4035)
+
+#else // !(defined(_TARGET_X86_)
+
+#pragma warning(disable:4273)
+extern "C" char* __cdecl _ecvt(double, int, int*, int*);
+
+void DoubleToNumber(double value, int precision, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+
+#ifndef FEATURE_BCL_FORMATTING
+ number->palNumber=PAL_DoubleToNumber(value);
+ IfNullThrow(number->palNumber);
+ number->palNumberType=PALNUMBERTYPE_DOUBLE;
+#endif
+
+ number->precision = precision;
+ if (((FPDOUBLE*)&value)->exp == 0x7FF) {
+ number->scale = (((FPDOUBLE*)&value)->mantLo || ((FPDOUBLE*)&value)->mantHi) ? SCALE_NAN: SCALE_INF;
+ number->sign = ((FPDOUBLE*)&value)->sign;
+ number->digits[0] = 0;
+ }
+ else {
+ char* src = _ecvt(value, precision, &number->scale, &number->sign);
+ wchar* dst = number->digits;
+ if (*src != '0') {
+ while (*src) *dst++ = *src++;
+ }
+ *dst = 0;
+ }
+}
+
+/*===========================================================
+ 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);
+}
+
+wchar_t* COMNumber::Int32ToDecChars(__in wchar_t* p, unsigned int value, int digits)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(p != NULL);
+
+ while (--digits >= 0 || value != 0) {
+ *--p = value % 10 + '0';
+ value /= 10;
+ }
+ return p;
+}
+
+unsigned int Int64DivMod1E9(unsigned __int64* value)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(value != NULL);
+
+ unsigned int rem = (unsigned int)(*value % 1000000000);
+ *value /= 1000000000;
+ return rem;
+}
+
+
+
+
+#endif // !(defined(_TARGET_X86_)
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
+#endif
+
+inline void AddStringRef(__in wchar** ppBuffer, STRINGREF strRef)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(ppBuffer != NULL && strRef != NULL);
+
+ wchar* buffer = strRef->GetBuffer();
+ _ASSERTE(buffer != NULL);
+ DWORD length = strRef->GetStringLength();
+ for (wchar* str = buffer; str < buffer + length; (*ppBuffer)++, str++)
+ {
+ **ppBuffer = *str;
+ }
+}
+
+inline wchar* GetDigitsBuffer(NUMBER* number)
+{
+ return (number->allDigits != NULL) ? number->allDigits : number->digits;
+}
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("", on) // Go back to command line default optimizations
+#endif
+
+LPCWSTR MatchChars(LPCWSTR p, LPCWSTR str)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(p != NULL && str != NULL);
+
+ if (!*str) return 0;
+ for (; *str; p++, str++)
+ {
+ if (*p != *str) //We only hurt the failure case
+ {
+ if ((*str == 0xA0) && (*p == 0x20)) // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
+ // space character we use 0x20 space character instead to mean the same.
+ continue;
+ return 0;
+ }
+ }
+ return p;
+}
+
+wchar* Int32ToHexChars(__in wchar* p, unsigned int value, int hexBase, int digits)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(p != NULL);
+
+ while (--digits >= 0 || value != 0) {
+ unsigned char digit = static_cast<unsigned char>(value & 0xF);
+ *--p = static_cast<wchar>(digit + (digit < 10? '0': hexBase));
+ value >>= 4;
+ }
+ return p;
+}
+
+STRINGREF Int32ToDecStr(int value, int digits, STRINGREF sNegative)
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ CQuickBytes buf;
+
+ if (digits < 1) digits = 1;
+
+ UINT maxDigitsLength = (digits > 15) ? digits : 15; // Since an int32 can have maximum of 10 chars as a String
+ UINT bufferLength = (maxDigitsLength > 100) ? maxDigitsLength : 100;
+ int negLength = 0;
+ wchar* src = NULL;
+
+ if (value < 0) {
+ _ASSERTE(sNegative != NULL);
+ src = sNegative->GetBuffer();
+ _ASSERTE(src != NULL);
+ negLength = sNegative->GetStringLength();
+ if ((UINT) negLength > bufferLength - maxDigitsLength) {
+ bufferLength = (UINT) negLength + maxDigitsLength;
+ }
+ }
+
+ wchar *buffer = (wchar*)buf.AllocThrows(bufferLength * sizeof(WCHAR));
+ wchar* p = COMNumber::Int32ToDecChars(buffer + bufferLength, value >= 0? value: -value, digits);
+ _ASSERTE(p != NULL);
+ if (value < 0) {
+ for (int i =negLength - 1; i >= 0; i--)
+ {
+ *(--p) = *(src+i);
+ }
+ }
+
+ _ASSERTE(buffer + bufferLength - p >=0 && buffer <= p);
+ return StringObject::NewString(p, (int)(buffer + bufferLength - p));
+}
+
+STRINGREF UInt32ToDecStr(unsigned int value, int digits)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[100];
+ if (digits < 1) digits = 1;
+ wchar* p = COMNumber::Int32ToDecChars(buffer + 100, value, digits);
+ _ASSERTE(p != NULL && p >= buffer && p < (buffer + 100));
+ return StringObject::NewString(p, (int) (buffer + 100 - p));
+}
+
+STRINGREF Int32ToHexStr(unsigned int value, int hexBase, int digits)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[100];
+ if (digits < 1) digits = 1;
+ wchar* p = Int32ToHexChars(buffer + 100, value, hexBase, digits);
+ return StringObject::NewString(p, (int) (buffer + 100 - p));
+}
+
+void Int32ToNumber(int value, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+
+#ifndef FEATURE_BCL_FORMATTING
+ number->palNumber=PAL_IntToNumber(value);
+ IfNullThrow(number->palNumber);
+ number->palNumberType=PALNUMBERTYPE_INT;
+#endif
+
+
+ wchar buffer[INT32_PRECISION+1];
+ number->precision = INT32_PRECISION;
+ if (value >= 0) {
+ number->sign = 0;
+ }
+ else {
+ number->sign = 1;
+ value = -value;
+ }
+ wchar* p = COMNumber::Int32ToDecChars(buffer + INT32_PRECISION, value, 0);
+ _ASSERTE(p != NULL);
+ int i = (int) (buffer + INT32_PRECISION - p);
+ number->scale = i;
+ wchar* dst = number->digits;
+ _ASSERTE(dst != NULL);
+ while (--i >= 0) *dst++ = *p++;
+ *dst = 0;
+
+
+}
+
+void UInt32ToNumber(unsigned int value, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+
+#ifndef FEATURE_BCL_FORMATTING
+ number->palNumber=PAL_UIntToNumber(value);
+ IfNullThrow(number->palNumber);
+ number->palNumberType=PALNUMBERTYPE_UINT;
+#endif
+
+ wchar buffer[UINT32_PRECISION+1];
+ number->precision = UINT32_PRECISION;
+ number->sign = 0;
+ wchar* p = COMNumber::Int32ToDecChars(buffer + UINT32_PRECISION, value, 0);
+ _ASSERT(p != NULL);
+ int i = (int) (buffer + UINT32_PRECISION - p);
+ number->scale = i;
+ wchar* dst = number->digits;
+ _ASSERT(dst != NULL);
+ while (--i >= 0) *dst++ = *p++;
+ *dst = 0;
+}
+
+
+
+
+#define LO32(x) ((unsigned int)(x))
+#define HI32(x) ((unsigned int)(((x) & UI64(0xFFFFFFFF00000000)) >> 32))
+
+STRINGREF Int64ToDecStr(__int64 value, int digits, STRINGREF sNegative)
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ CQuickBytes buf;
+
+ if (digits < 1) digits = 1;
+ int sign = HI32(value);
+
+ // digits as specified in the format string can be at most 99.
+ UINT maxDigitsLength = (digits > 20) ? digits : 20;
+ UINT bufferLength = (maxDigitsLength > 100) ? maxDigitsLength : 100;
+
+ if (sign < 0) {
+ value = -value;
+ _ASSERTE(sNegative);
+ int negLength = sNegative->GetStringLength();
+ if ((UINT) negLength > bufferLength - maxDigitsLength) {
+ bufferLength = negLength + maxDigitsLength;
+ }
+ }
+
+ wchar *buffer = (wchar*)buf.AllocThrows(bufferLength * sizeof(WCHAR));
+ wchar* p = buffer + bufferLength;
+ while (HI32(value)) {
+ p = COMNumber::Int32ToDecChars(p, Int64DivMod1E9((unsigned __int64*)&value), 9);
+ _ASSERTE(p != NULL);
+ digits -= 9;
+ }
+ p = COMNumber::Int32ToDecChars(p, LO32(value), digits);
+ _ASSERTE(p != NULL);
+ if (sign < 0) {
+ wchar* src = sNegative->GetBuffer();
+ _ASSERTE(src != NULL);
+ for (int i =sNegative->GetStringLength() - 1; i >= 0; i--)
+ {
+ *(--p) = *(src+i);
+ }
+ }
+ return StringObject::NewString(p, (int) (buffer + bufferLength - p));
+}
+
+STRINGREF UInt64ToDecStr(unsigned __int64 value, int digits)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[100];
+ if (digits < 1) digits = 1;
+ wchar* p = buffer + 100;
+ while (HI32(value)) {
+ p = COMNumber::Int32ToDecChars(p, Int64DivMod1E9(&value), 9);
+ _ASSERTE(p != NULL);
+ digits -= 9;
+ }
+ p = COMNumber::Int32ToDecChars(p, LO32(value), digits);
+ _ASSERTE(p != NULL && p >= buffer && p < (buffer + 100));
+ return StringObject::NewString(p, (int) (buffer + 100 - p));
+}
+
+STRINGREF Int64ToHexStr(unsigned __int64 value, int hexBase, int digits)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[100];
+ wchar* p;
+ if (HI32(value)) {
+ Int32ToHexChars(buffer + 100, LO32(value), hexBase, 8);
+ p = Int32ToHexChars(buffer + 100 - 8, HI32(value), hexBase, digits - 8);
+ }
+ else {
+ if (digits < 1) digits = 1;
+ p = Int32ToHexChars(buffer + 100, LO32(value), hexBase, digits);
+ }
+ _ASSERTE(p != NULL && p >= buffer && p < (buffer + 100));
+ return StringObject::NewString(p, (int) (buffer + 100 - p));
+}
+
+void Int64ToNumber(__int64 value, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+
+#ifndef FEATURE_BCL_FORMATTING
+ number->palNumber=PAL_Int64ToNumber(value);
+ IfNullThrow(number->palNumber);
+ number->palNumberType=PALNUMBERTYPE_INT64;
+#endif
+
+ wchar buffer[INT64_PRECISION+1];
+ number->precision = INT64_PRECISION;
+ if (value >= 0) {
+ number->sign = 0;
+ }
+ else {
+ number->sign = 1;
+ value = -value;
+ }
+ wchar* p = buffer + INT64_PRECISION;
+ while (HI32(value)) {
+ p = COMNumber::Int32ToDecChars(p, Int64DivMod1E9((unsigned __int64*)&value), 9);
+ _ASSERTE(p != NULL);
+ }
+ p = COMNumber::Int32ToDecChars(p, LO32(value), 0);
+ _ASSERTE(p != NULL);
+ int i = (int) (buffer + INT64_PRECISION - p);
+ number->scale = i;
+ wchar* dst = number->digits;
+ _ASSERTE(dst != NULL);
+ while (--i >= 0) *dst++ = *p++;
+ *dst = 0;
+
+}
+
+void UInt64ToNumber(unsigned __int64 value, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[UINT64_PRECISION+1];
+ number->precision = UINT64_PRECISION;
+ number->sign = 0;
+ wchar* p = buffer + UINT64_PRECISION;
+ while (HI32(value)) {
+ p = COMNumber::Int32ToDecChars(p, Int64DivMod1E9(&value), 9);
+ _ASSERTE(p != NULL);
+ }
+ p = COMNumber::Int32ToDecChars(p, LO32(value), 0);
+ _ASSERTE(p != NULL);
+ int i = (int) (buffer + UINT64_PRECISION - p);
+ number->scale = i;
+ wchar* dst = number->digits;
+ _ASSERTE(dst != NULL);
+ while (--i >= 0) *dst++ = *p++;
+ *dst = 0;
+
+}
+
+#ifndef FEATURE_BCL_FORMATTING
+void NumberToUInt64(NUMBER * number, unsigned __int64* value)
+{
+ _ASSERTE(NULL != number);
+ _ASSERTE(NULL != value);
+
+ if (NULL != number && NULL != value) {
+ (*value) = 0;
+ int i = 0;
+ while (i < NUMBER_MAXDIGITS && i < number->precision && number->digits[i] != NULL) {
+ _ASSERTE((number->digits[i] - '0') >= 0 && (number->digits[i] - '0') <= 9);
+ *value = (10 * (*value)) + (number->digits[i] - '0');
+ i++;
+ }
+ while (i < number->scale) {
+ *value = (10 * (*value));
+ i++;
+ }
+ }
+}
+#endif
+
+
+void RoundNumber(NUMBER* number, int pos)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(number != NULL);
+
+ wchar_t* digits = GetDigitsBuffer(number);
+ int i = 0;
+ while (i < pos && digits[i] != 0) i++;
+ if (i == pos && digits[i] >= '5') {
+ while (i > 0 && digits[i - 1] == '9') i--;
+ if (i > 0) {
+ digits[i - 1]++;
+ }
+ else {
+ number->scale++;
+ digits[0] = '1';
+ i = 1;
+ }
+ }
+ else {
+ while (i > 0 && digits[i - 1] == '0') i--;
+ }
+ if (i == 0) {
+ number->scale = 0;
+ number->sign = 0;
+ }
+ digits[i] = 0;
+
+#ifndef FEATURE_BCL_FORMATTING
+//
+// The PAL stores PALNUMBER as the actual numeric type where as NUMBER is in string form;
+// Convert NUMBER back into its original type and pass it to the PAL for later use
+//
+ if (0 != number->palNumber) {
+ if (PALNUMBERTYPE_DOUBLE == number->palNumberType) {
+ // no need to round NaN or infinity double values
+ if (SCALE_NAN != ((unsigned int)number->scale) && SCALE_INF != ((unsigned int)number->scale)) {
+ double value = 0.0;
+ NumberToDouble(number, &value);
+ // make sure the rounding didn't accidently cause the good value
+ // to be turned into NaN or infinity
+ if (((FPDOUBLE*)&value)->exp != 0x7FF) {
+ number->palNumber=PAL_DoubleToNumber(value);
+ }
+ }
+ }
+ else {
+ unsigned __int64 value = 0;
+ NumberToUInt64(number, &value);
+ switch(number->palNumberType) {
+ case PALNUMBERTYPE_INT:
+ _ASSERTE((value >> 32) == 0);
+ number->palNumber=PAL_IntToNumber(((unsigned int)value) * (number->sign ? -1 : 1));
+ break;
+ case PALNUMBERTYPE_UINT:
+ _ASSERTE((value >> 32) == 0);
+ number->palNumber=PAL_UIntToNumber((unsigned int)value);
+ break;
+ case PALNUMBERTYPE_INT64:
+ _ASSERTE((value >> 63) == 0);
+ number->palNumber=PAL_Int64ToNumber(((__int64)value) * (number->sign ? -1 : 1));
+ break;
+ case PALNUMBERTYPE_UINT64:
+ number->palNumber=PAL_UInt64ToNumber(value);
+ break;
+ default:
+ CONSISTENCY_CHECK_MSGF(0, ("This palNumberType is not understood '(%d)''\n", number->palNumberType));
+ break;
+ }
+ }
+ }
+#endif
+}
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
+#endif
+
+wchar ParseFormatSpecifier(STRINGREF str, int* digits)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(digits != NULL);
+
+ if (str != 0) {
+ wchar* p = str->GetBuffer();
+ _ASSERTE(p != NULL);
+ wchar ch = *p;
+ if (ch != 0) {
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
+ p++;
+ int n = -1;
+ if (*p >= '0' && *p <= '9') {
+ n = *p++ - '0';
+ while (*p >= '0' && *p <= '9') {
+ n = n * 10 + *p++ - '0';
+ if (n >= 10) break;
+ }
+ }
+ if (*p == 0) {
+ *digits = n;
+ return ch;
+ }
+ }
+ return 0;
+ }
+ }
+ *digits = -1;
+ return 'G';
+}
+
+wchar* FormatExponent(__in wchar* buffer, int value, wchar expChar,
+ STRINGREF posSignStr, STRINGREF negSignStr, int minDigits)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(buffer != NULL);
+
+ wchar digits[11];
+ *buffer++ = expChar;
+ if (value < 0) {
+ _ASSERTE(negSignStr != NULL);
+ AddStringRef(&buffer, negSignStr);
+ value = -value;
+ }
+ else {
+ if (posSignStr!= NULL) {
+ AddStringRef(&buffer, posSignStr);
+ }
+ }
+ wchar* p = COMNumber::Int32ToDecChars(digits + 10, value, minDigits);
+ _ASSERTE(p != NULL);
+ int i = (int) (digits + 10 - p);
+ while (--i >= 0) *buffer++ = *p++;
+ return buffer;
+}
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("", on) // Go back to command line default optimizations
+#endif
+
+wchar* FormatGeneral(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits, wchar expChar,
+ STRINGREF sNumberDecimal, STRINGREF sPositive, STRINGREF sNegative, STRINGREF sZero, BOOL bSuppressScientific = FALSE)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+ _ASSERTE(buffer != NULL);
+
+ int digPos = number->scale;
+ int scientific = 0;
+ if (!bSuppressScientific) { // Don't switch to scientific notation
+ if (digPos > nMaxDigits || digPos < -3) {
+ digPos = 1;
+ scientific = 1;
+ }
+ }
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ WCHAR sExp[2]={expChar};
+ LPCWSTR strNumberDecimal(sNumberDecimal!=NULL?sNumberDecimal->GetBuffer():NULL);
+ LPCWSTR strPositive(sPositive!=NULL?sPositive->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+
+ int nChars;
+
+ // nMaxDigits for Scientific are 1 more than needed
+ if (scientific)
+ nChars=PAL_FormatScientific(NULL, buffer, cchBuffer, number->palNumber,-1,nMaxDigits-1,sExp,strNumberDecimal,strPositive,strNegative,strZero);
+ else
+ nChars=PAL_FormatDecimal(NULL, buffer, cchBuffer, number->palNumber,-1,nMaxDigits,-1, -1,-1,strNumberDecimal,NULL,strNegative,strZero);
+
+ if(nChars<0)
+ return NULL;
+ return buffer+nChars;
+ }
+#endif
+
+ wchar* dig = GetDigitsBuffer(number);
+ _ASSERT(dig != NULL);
+ if (digPos > 0) {
+ do {
+ *buffer++ = *dig != 0? *dig++: '0';
+ } while (--digPos > 0);
+ }
+ else {
+ *buffer++ = '0';
+ }
+ if (*dig != 0 || digPos < 0) {
+ AddStringRef(&buffer, sNumberDecimal);
+ while (digPos < 0) {
+ *buffer++ = '0';
+ digPos++;
+ }
+ while (*dig != 0) {
+ *buffer++ = *dig++;
+ }
+ }
+ if (scientific) buffer = FormatExponent(buffer, number->scale - 1, expChar, sPositive, sNegative, 2);
+ return buffer;
+}
+
+wchar* FormatScientific(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits, wchar expChar,
+ STRINGREF sNumberDecimal, STRINGREF sPositive, STRINGREF sNegative, STRINGREF sZero)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+ _ASSERTE(buffer != NULL);
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ WCHAR sExp[2]={expChar};
+ LPCWSTR strNumberDecimal(sNumberDecimal!=NULL?sNumberDecimal->GetBuffer():NULL);
+ LPCWSTR strPositive(sPositive!=NULL?sPositive->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+
+ // nMaxDigits passed into FormatScientific are 1 more than requested
+ int nChars=PAL_FormatScientific(NULL, buffer, cchBuffer, number->palNumber,nMinDigits, nMaxDigits-1,sExp,strNumberDecimal,strPositive,strNegative,strZero);
+ if(nChars<0)
+ ThrowLastError();
+ return buffer+nChars;
+ }
+#endif
+
+
+ wchar* dig = GetDigitsBuffer(number);
+ _ASSERTE(dig != NULL);
+ *buffer++ = *dig != 0? *dig++: '0';
+ if (nMaxDigits != 1) // For E0 we would like to suppress the decimal point
+ AddStringRef(&buffer, sNumberDecimal);
+ while (--nMaxDigits > 0) *buffer++ = *dig != 0? *dig++: '0';
+ int e = (GetDigitsBuffer(number))[0] == 0 ? 0 : number->scale - 1;
+ buffer = FormatExponent(buffer, e, expChar, sPositive, sNegative, 3);
+ _ASSERTE(buffer != NULL);
+ return buffer;
+}
+
+wchar* FormatFixed(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits,
+ I4ARRAYREF groupDigitsRef, STRINGREF sDecimal, STRINGREF sGroup, STRINGREF sNegative,STRINGREF sZero)
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ LPCWSTR strDecimal(sDecimal!=NULL?sDecimal->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+
+ int nChars=PAL_FormatDecimal(NULL, buffer, cchBuffer, number->palNumber,nMinDigits,nMaxDigits,-1, -1,-1,strDecimal,W(""),strNegative,strZero);
+ if(nChars<0)
+ return NULL;
+ return buffer+nChars;
+ }
+#endif
+
+
+ int digPos = number->scale;
+ wchar* dig = GetDigitsBuffer(number);
+ const I4* groupDigits = NULL;
+ if (groupDigitsRef != NULL) {
+ groupDigits = groupDigitsRef->GetDirectConstPointerToNonObjectElements();
+ }
+
+ if (digPos > 0) {
+ if (groupDigits != NULL) {
+
+ int groupSizeIndex = 0; // index into the groupDigits array.
+ int groupSizeCount = groupDigits[groupSizeIndex]; // the current total of group size.
+ int groupSizeLen = groupDigitsRef->GetNumComponents(); // the length of groupDigits array.
+ int bufferSize = digPos; // the length of the result buffer string.
+ int groupSeparatorLen = sGroup->GetStringLength(); // the length of the group separator string.
+ int groupSize = 0; // the current group size.
+
+ //
+ // Find out the size of the string buffer for the result.
+ //
+ if (groupSizeLen != 0) // You can pass in 0 length arrays
+ {
+ while (digPos > groupSizeCount) {
+ groupSize = groupDigits[groupSizeIndex];
+ if (groupSize == 0) {
+ break;
+ }
+
+ bufferSize += groupSeparatorLen;
+ if (groupSizeIndex < groupSizeLen - 1) {
+ groupSizeIndex++;
+ }
+ groupSizeCount += groupDigits[groupSizeIndex];
+ if (groupSizeCount < 0 || bufferSize < 0) {
+ COMPlusThrow(kArgumentOutOfRangeException); // if we overflow
+ }
+ }
+ if (groupSizeCount == 0) // If you passed in an array with one entry as 0, groupSizeCount == 0
+ groupSize = 0;
+ else
+ groupSize = groupDigits[0];
+ }
+
+ groupSizeIndex = 0;
+ int digitCount = 0;
+ int digStart;
+ int digLength = (int)wcslen(dig);
+ digStart = (digPos<digLength)?digPos:digLength;
+ wchar* p = buffer + bufferSize - 1;
+ for (int i = digPos - 1; i >=0; i--) {
+ *(p--) = (i<digStart)?dig[i]:'0';
+
+ if (groupSize > 0) {
+ digitCount++;
+ if (digitCount == groupSize && i != 0) {
+ for (int j = groupSeparatorLen - 1; j >=0; j--) {
+ *(p--) = sGroup->GetBuffer()[j];
+ }
+
+ if (groupSizeIndex < groupSizeLen - 1) {
+ groupSizeIndex++;
+ groupSize = groupDigits[groupSizeIndex];
+ }
+ digitCount = 0;
+ }
+ }
+ }
+ if (p < buffer - 1) {
+ // This indicates a buffer underflow since we write in backwards.
+ DoJITFailFast();
+ }
+ buffer += bufferSize;
+ dig += digStart;
+ } else {
+ do {
+ *buffer++ = *dig != 0? *dig++: '0';
+ } while (--digPos > 0);
+ }
+ }
+ else {
+ *buffer++ = '0';
+ }
+ if (nMaxDigits > 0) {
+ AddStringRef(&buffer, sDecimal);
+ while (digPos < 0 && nMaxDigits > 0) {
+ *buffer++ = '0';
+ digPos++;
+ nMaxDigits--;
+ }
+ while (nMaxDigits > 0) {
+ *buffer++ = *dig != 0? *dig++: '0';
+ nMaxDigits--;
+ }
+ }
+ return buffer;
+}
+
+wchar* FormatNumber(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits, int cNegativeNumberFormat, I4ARRAYREF cNumberGroup, STRINGREF sNumberDecimal, STRINGREF sNumberGroup, STRINGREF sNegative, STRINGREF sZero)
+{
+ CONTRACTL {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ LPCWSTR strDecimal(sNumberDecimal!=NULL?sNumberDecimal->GetBuffer():NULL);
+ LPCWSTR strGroup(sNumberGroup!=NULL?sNumberGroup->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+ int iPrimaryGroup=0;
+ int iSecondaryGroup=0;
+ if(cNumberGroup!=NULL)
+ {
+ int nGroups = cNumberGroup->GetNumComponents();
+ I4* pGroups=(I4*)cNumberGroup->GetDataPtr();
+
+ if(nGroups>0)
+ iPrimaryGroup=pGroups[0];
+ if(nGroups>1)
+ iSecondaryGroup=pGroups[1];
+ }
+
+ int nChars=PAL_FormatDecimal(NULL, buffer, cchBuffer, number->palNumber,nMinDigits,nMaxDigits, cNegativeNumberFormat,
+ iPrimaryGroup,iSecondaryGroup,strDecimal,strGroup,strNegative,strZero);
+ if(nChars<0)
+ return NULL;
+ return buffer+nChars;
+ }
+#endif
+
+ char ch;
+ const char* fmt;
+ fmt = number->sign?
+ negNumberFormats[cNegativeNumberFormat]:
+ posNumberFormat;
+
+ while ((ch = *fmt++) != 0) {
+ switch (ch) {
+ case '#':
+ buffer = FormatFixed(buffer, cchBuffer, number, nMinDigits,nMaxDigits,
+ cNumberGroup,
+ sNumberDecimal, sNumberGroup,sNegative,sZero);
+ break;
+ case '-':
+ AddStringRef(&buffer, sNegative);
+ break;
+ default:
+ *buffer++ = ch;
+ }
+ }
+ return buffer;
+
+}
+
+wchar* FormatCurrency(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits,int nMaxDigits, int cNegCurrencyFormat, int cPosCurrencyFormat, I4ARRAYREF cCurrencyGroup,
+ STRINGREF sCurrencyDecimal, STRINGREF sCurrencyGroup, STRINGREF sNegative, STRINGREF sCurrency,STRINGREF sZero)
+{
+ CONTRACTL {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ LPCWSTR strCurrencyDecimal(sCurrencyDecimal!=NULL?sCurrencyDecimal->GetBuffer():NULL);
+ LPCWSTR strCurrencyGroup(sCurrencyGroup!=NULL?sCurrencyGroup->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strCurrency(sCurrency!=NULL?sCurrency->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+ int iPrimaryGroup=0;
+ int iSecondaryGroup=0;
+
+ if(cCurrencyGroup!=NULL)
+ {
+ int nGroups = cCurrencyGroup->GetNumComponents();
+ I4* pGroups=(I4*)cCurrencyGroup->GetDataPtr();
+
+ if(nGroups>0)
+ iPrimaryGroup=pGroups[0];
+ if(nGroups>1)
+ iSecondaryGroup=pGroups[1];
+ }
+
+ int nChars=PAL_FormatCurrency(NULL, buffer, cchBuffer, number->palNumber,nMinDigits,nMaxDigits,cNegCurrencyFormat, cPosCurrencyFormat,
+ iPrimaryGroup, iSecondaryGroup, strCurrencyDecimal,strCurrencyGroup,strNegative, strCurrency,strZero);
+ if (nChars<0)
+ return NULL;
+
+ return buffer+nChars;
+ }
+#endif
+ char ch;
+ const char* fmt;
+ fmt = number->sign?
+ negCurrencyFormats[cNegCurrencyFormat]:
+ posCurrencyFormats[cPosCurrencyFormat];
+
+ while ((ch = *fmt++) != 0) {
+ switch (ch) {
+ case '#':
+ buffer = FormatFixed(buffer, cchBuffer, number, nMinDigits,nMaxDigits,
+ cCurrencyGroup,
+ sCurrencyDecimal, sCurrencyGroup,sNegative,sZero);
+ break;
+ case '-':
+ AddStringRef(&buffer, sNegative);
+ break;
+ case '$':
+ AddStringRef(&buffer, sCurrency);
+ break;
+ default:
+ *buffer++ = ch;
+ }
+ }
+ return buffer;
+}
+
+wchar* FormatPercent(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits, int cNegativePercentFormat, int cPositivePercentFormat, I4ARRAYREF cPercentGroup,
+ STRINGREF sPercentDecimal, STRINGREF sPercentGroup, STRINGREF sNegative, STRINGREF sPercent, STRINGREF sZero)
+{
+ CONTRACTL {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ LPCWSTR strPercentDecimal(sPercentDecimal!=NULL?sPercentDecimal->GetBuffer():NULL);
+ LPCWSTR strPercentGroup(sPercentGroup!=NULL?sPercentGroup->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strPercent(sPercent!=NULL?sPercent->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+
+ int iPrimaryGroup=0;
+ int iSecondaryGroup=0;
+
+ if(cPercentGroup!=NULL)
+ {
+ int nGroups = cPercentGroup->GetNumComponents();
+ I4* pGroups=(I4*)cPercentGroup->GetDataPtr();
+
+ if(nGroups>0)
+ iPrimaryGroup=pGroups[0];
+ if(nGroups>1)
+ iSecondaryGroup=pGroups[1];
+ }
+
+ int nChars=PAL_FormatPercent(NULL, buffer, cchBuffer, number->palNumber,nMinDigits,nMaxDigits,cNegativePercentFormat, cPositivePercentFormat,
+ iPrimaryGroup, iSecondaryGroup,strPercentDecimal,strPercentGroup,strNegative, strPercent,strZero);
+ if(nChars<0)
+ return NULL;
+
+ return buffer+nChars;
+ }
+#endif
+
+ char ch;
+ const char* fmt;
+ fmt = number->sign?
+ negPercentFormats[cNegativePercentFormat]:
+ posPercentFormats[cPositivePercentFormat];
+
+ while ((ch = *fmt++) != 0) {
+ switch (ch) {
+ case '#':
+ buffer = FormatFixed(buffer, cchBuffer, number, nMinDigits,nMaxDigits,
+ cPercentGroup,
+ sPercentDecimal, sPercentGroup,sNegative,sZero);
+ break;
+ case '-':
+ AddStringRef(&buffer, sNegative);
+ break;
+ case '%':
+ AddStringRef(&buffer, sPercent);
+ break;
+ default:
+ *buffer++ = ch;
+ }
+ }
+ return buffer;
+}
+
+STRINGREF NumberToString(NUMBER* number, wchar format, int nMaxDigits, NUMFMTREF numfmt, BOOL bDecimal = FALSE )
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+
+ int nMinDigits=-1;
+ STRINGREF sZero=NULL;
+
+ // @TODO what if not sequential?
+#ifndef FEATURE_CORECLR
+ if (numfmt->iDigitSubstitution == 2) // native digits
+ {
+ PTRARRAYREF aDigits = numfmt->sNativeDigits;
+ if (aDigits!=NULL && aDigits->GetNumComponents()>0)
+ sZero=(STRINGREF)aDigits->GetAt(0);
+ }
+#endif
+
+ // Do the worst case calculation
+ /* US English - for Double.MinValue.ToString("C99"); we require 514 characters
+ ----------
+ 2 paranthesis
+ 1 currency character
+ 308 characters
+ 103 group seperators
+ 1 decimal separator
+ 99 0's
+
+ digPos + 99 + 6(slack) => digPos + 105
+ C
+ sNegative
+ sCurrencyGroup
+ sCurrencyDecimal
+ sCurrency
+ F
+ sNegative
+ sNumberDecimal
+ N
+ sNegative
+ sNumberDecimal
+ sNumberGroup
+ E
+ sNegative
+ sPositive
+ sNegative (for exponent)
+ sPositive
+ sNumberDecimal
+ G
+ sNegative
+ sPositive
+ sNegative (for exponent)
+ sPositive
+ sNumberDecimal
+ P (+2 for some spaces)
+ sNegative
+ sPercentGroup
+ sPercentDecimal
+ sPercent
+ */
+
+ _ASSERTE(numfmt != NULL);
+ UINT64 newBufferLen = MIN_BUFFER_SIZE;
+
+ CQuickBytesSpecifySize<LARGE_BUFFER_SIZE * sizeof(WCHAR)> buf;
+
+ wchar *buffer = NULL;
+ wchar* dst = NULL;
+ wchar ftype = format & 0xFFDF;
+ int digCount = 0;
+
+ switch (ftype) {
+ case 'C':
+ {
+ nMinDigits = nMaxDigits >= 0 ? nMaxDigits : numfmt->cCurrencyDecimals;
+
+ if (nMaxDigits< 0)
+ nMaxDigits = numfmt->cCurrencyDecimals;
+ if (number->scale < 0)
+ digCount = 0;
+ else if (!ClrSafeInt<INT32>::addition(number->scale, nMaxDigits, digCount))
+ COMPlusThrowOM();
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ int cNegCurrencyFormat = numfmt->cNegCurrencyFormat;
+ int cPosCurrencyFormat = numfmt->cPosCurrencyFormat;
+ I4ARRAYREF cCurrencyGroup = numfmt->cCurrencyGroup;
+ STRINGREF sCurrencyDecimal = numfmt->sCurrencyDecimal;
+ STRINGREF sCurrencyGroup = numfmt->sCurrencyGroup;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sCurrency = numfmt->sCurrency;
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatCurrency uses BCL format
+ number->palNumber = 0;
+#endif
+ // Prefix: bogus warning 22011: newBufferLen+=digCount may be smaller than MIN_BUFFER_SIZE
+ PREFIX_ASSUME(digCount >=0 && digCount <= INT32_MAX);
+ newBufferLen += digCount;
+ newBufferLen += sNegative->GetStringLength(); // For number and exponent
+ if (!ClrSafeInt<UINT64>::addition((UINT64)sCurrencyGroup->GetStringLength() * digCount, newBufferLen, newBufferLen))
+ COMPlusThrowOM();
+ newBufferLen += sCurrencyDecimal->GetStringLength();
+ newBufferLen += sCurrency->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, number->scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed.
+ dst = FormatCurrency(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)), number, nMinDigits,nMaxDigits, cNegCurrencyFormat, cPosCurrencyFormat, cCurrencyGroup, sCurrencyDecimal, sCurrencyGroup, sNegative, sCurrency,sZero);
+
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for ( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatCurrency(dst, nChars, number, nMinDigits, nMaxDigits, cNegCurrencyFormat, cPosCurrencyFormat, cCurrencyGroup, sCurrencyDecimal, sCurrencyGroup, sNegative, sCurrency,sZero);
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ break;
+ }
+ case 'F':
+ {
+ if (nMaxDigits< 0)
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = numfmt->cNumberDecimals;
+ else
+ nMinDigits=nMaxDigits;
+
+ if (number->scale < 0)
+ digCount = 0;
+ else
+ digCount = number->scale + nMaxDigits;
+
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sNegative = numfmt->sNegative;
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatFixed uses BCL format
+ number->palNumber = 0;
+#endif
+
+ newBufferLen += digCount;
+ newBufferLen += sNegative->GetStringLength(); // For number and exponent
+ newBufferLen += sNumberDecimal->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, number->scale + nMaxDigits);
+ if (number->sign) {
+ AddStringRef(&dst, sNegative);
+ }
+ dst = FormatFixed(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)-(dst-buffer)), number, nMinDigits,nMaxDigits,
+ NULL,
+ sNumberDecimal, NULL, sNegative, sZero);
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatFixed(dst, nChars, number, nMinDigits,nMaxDigits,
+ NULL,
+ sNumberDecimal, NULL,sNegative,sZero);
+
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ break;
+ }
+ case 'N':
+ {
+ if (nMaxDigits < 0)
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = numfmt->cNumberDecimals; // Since we are using digits in our calculation
+ else
+ nMinDigits=nMaxDigits;
+
+ if (number->scale < 0)
+ digCount = 0;
+ else
+ digCount = number->scale + nMaxDigits;
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ I4ARRAYREF cNumberGroup = numfmt->cNumberGroup;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sNumberGroup = numfmt->sNumberGroup;
+ int cNegativeNumberFormat = numfmt->cNegativeNumberFormat;
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatNumber uses BCL format
+ number->palNumber = 0;
+#endif
+
+ newBufferLen += digCount;
+ newBufferLen += sNegative->GetStringLength(); // For number and exponent
+ if (!ClrSafeInt<UINT64>::addition((UINT64)sNumberGroup->GetStringLength() * digCount, newBufferLen, newBufferLen))
+ COMPlusThrowOM();
+ newBufferLen += sNumberDecimal->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, number->scale + nMaxDigits);
+ dst = FormatNumber(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)),number, nMinDigits, nMaxDigits, cNegativeNumberFormat, cNumberGroup, sNumberDecimal, sNumberGroup, sNegative, sZero);
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatNumber(dst, nChars, number, nMinDigits,nMaxDigits, cNegativeNumberFormat, cNumberGroup, sNumberDecimal, sNumberGroup, sNegative,sZero);
+
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ break;
+ }
+ case 'E':
+ {
+ // It is critical to format with the same values that we use to calculate buffer size.
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sPositive = numfmt->sPositive;
+
+ if (nMaxDigits < 0)
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = 6;
+ else
+ nMinDigits=nMaxDigits;
+ nMaxDigits++;
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatScientific uses BCL format
+ number->palNumber = 0;
+#endif
+
+ newBufferLen += nMaxDigits;
+ newBufferLen += (((INT64)sNegative->GetStringLength() + sPositive->GetStringLength()) *2); // For number and exponent
+ newBufferLen += sNumberDecimal->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, nMaxDigits);
+ if (number->sign) {
+ AddStringRef(&dst, sNegative);
+ }
+ dst = FormatScientific(dst, static_cast<SIZE_T>(newBufferLen * sizeof(WCHAR)-(dst-buffer)),number, nMinDigits,nMaxDigits, format, sNumberDecimal, sPositive, sNegative,sZero);
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatScientific(dst, nChars, number, nMinDigits, nMaxDigits, format, sNumberDecimal, sPositive, sNegative,sZero);
+
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ break;
+ }
+ case 'G':
+ {
+ bool enableRounding = true;
+ if (nMaxDigits < 1) {
+ if (bDecimal && (nMaxDigits == -1)) { // Default to 29 digits precision only for G formatting without a precision specifier
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = DECIMAL_PRECISION;
+ enableRounding = false; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant
+ }
+ else {
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = number->precision;
+ }
+ }
+ else
+ nMinDigits=nMaxDigits;
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sPositive = numfmt->sPositive;
+ newBufferLen += nMaxDigits;
+ newBufferLen += (((INT64)sNegative->GetStringLength() + sPositive->GetStringLength()) *2); // For number and exponent
+ newBufferLen += sNumberDecimal->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ if (enableRounding) // Don't round for G formatting without precision
+ RoundNumber(number, nMaxDigits); // This also fixes up the minus zero case
+ else {
+ if (bDecimal && ((GetDigitsBuffer(number))[0] == 0)) { // Minus zero should be formatted as 0
+ number->sign = 0;
+ }
+ }
+ if (number->sign) {
+ AddStringRef(&dst, sNegative);
+ }
+
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatScientific uses BCL format
+ number->palNumber = 0;
+#endif
+
+ dst = FormatGeneral(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)), number, nMinDigits,nMaxDigits, format - ('G' - 'E'), sNumberDecimal, sPositive, sNegative, sZero, !enableRounding);
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatGeneral(dst, nChars, number, nMinDigits,nMaxDigits, format - ('G' - 'E'), sNumberDecimal, sPositive, sNegative, sZero, !enableRounding);
+
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ }
+ break;
+ case 'P':
+ {
+ if (nMaxDigits< 0)
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = numfmt->cPercentDecimals;
+ else
+ nMinDigits=nMaxDigits;
+ number->scale += 2;
+
+ if (number->scale < 0)
+ digCount = 0;
+ else
+ digCount = number->scale + nMaxDigits;
+
+
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ int cNegativePercentFormat = numfmt->cNegativePercentFormat;
+ int cPositivePercentFormat = numfmt->cPositivePercentFormat;
+ I4ARRAYREF cPercentGroup = numfmt->cPercentGroup;
+ STRINGREF sPercentDecimal = numfmt->sPercentDecimal;
+ STRINGREF sPercentGroup = numfmt->sPercentGroup;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sPercent = numfmt->sPercent;
+
+#ifndef FEATURE_BCL_FORMATTING
+ // So that FormatPercent uses BCL format
+ number->palNumber = 0;
+#endif
+ newBufferLen += digCount;
+ newBufferLen += sNegative->GetStringLength(); // For number and exponent
+ if (!ClrSafeInt<UINT64>::addition((UINT64)sPercentGroup->GetStringLength() * digCount, newBufferLen, newBufferLen))
+ COMPlusThrowOM();
+ newBufferLen += sPercentDecimal->GetStringLength();
+ newBufferLen += sPercent->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, number->scale + nMaxDigits);
+ dst = FormatPercent(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)),number, nMinDigits,nMaxDigits, cNegativePercentFormat, cPositivePercentFormat, cPercentGroup, sPercentDecimal, sPercentGroup, sNegative, sPercent, sZero);
+
+ break;
+ }
+ default:
+ COMPlusThrow(kFormatException, W("Argument_BadFormatSpecifier"));
+ }
+ // check for overflow of the preallocated buffer
+#ifdef FEATURE_BCL_FORMATTING // when not defined the buffer could be resized, so skip the check
+// Review signed/unsigned mismatch in '<=' comparison.
+#pragma warning(push)
+#pragma warning(disable:4018)
+ if (!((dst - buffer >= 0) && (dst - buffer) <= (newBufferLen / sizeof(WCHAR) ))) {
+#pragma warning(pop)
+ DoJITFailFast();
+ }
+#endif
+
+ return StringObject::NewString(buffer, (int) (dst - buffer));
+}
+
+LPCWSTR FindSection(LPCWSTR format, int section)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(format != NULL);
+
+ LPCWSTR src;
+ wchar ch;
+ if (section == 0) return format;
+ src = format;
+ for (;;) {
+ switch (ch = *src++) {
+ case '\'':
+ case '"':
+ while (*src != 0 && *src++ != ch);
+ break;
+ case '\\':
+ if (*src != 0) src++;
+ break;
+ case ';':
+ if (--section != 0) break;
+ if (*src != 0 && *src != ';') return src;
+ case 0:
+ return format;
+ }
+ }
+}
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+STRINGREF NumberToStringFormat(NUMBER* number, STRINGREF str, NUMFMTREF numfmt)
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ int digitCount;
+ int decimalPos;
+ int firstDigit;
+ int lastDigit;
+ int digPos;
+ int scientific;
+ int percent;
+ int permille;
+ int thousandPos;
+ int thousandCount = 0;
+ int thousandSeps;
+ int scaleAdjust;
+ int adjust;
+ wchar* format=NULL;
+ LPCWSTR section=NULL;
+ LPCWSTR src=NULL;
+ wchar* dst=NULL;
+ wchar* dig=NULL;
+ wchar ch;
+ wchar* buffer=NULL;
+ CQuickBytes buf;
+
+ _ASSERTE(str != NULL);
+ _ASSERTE(numfmt != NULL);
+
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sPositive = numfmt->sPositive;
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sPercent = numfmt->sPercent;
+ STRINGREF sPerMille = numfmt->sPerMille;
+ STRINGREF sNumberGroup = numfmt->sNumberGroup;
+ I4ARRAYREF cNumberGroup = numfmt->cNumberGroup;
+
+ format = str->GetBuffer();
+
+ section = FindSection(format, (GetDigitsBuffer(number))[0] == 0 ? 2 : number->sign ? 1 : 0);
+
+#ifndef FEATURE_BCL_FORMATTING
+ // custom formatting is all done in the VM without the PAL. Blanking
+ // the palNumber field avoids unnecessary RoundNumber calculations
+ number->palNumber = 0;
+#endif
+
+ParseSection:
+ digitCount = 0;
+ decimalPos = -1;
+ firstDigit = 0x7FFFFFFF;
+ lastDigit = 0;
+ scientific = 0;
+ percent = 0;
+ permille = 0;
+ thousandPos = -1;
+ thousandSeps = 0;
+ scaleAdjust = 0;
+ src = section;
+ _ASSERTE(src != NULL);
+ while ((ch = *src++) != 0 && ch != ';') {
+ switch (ch) {
+ case '#':
+ digitCount++;
+ break;
+ case '0':
+ if (firstDigit == 0x7FFFFFFF) firstDigit = digitCount;
+ digitCount++;
+ lastDigit = digitCount;
+ break;
+ case '.':
+ if (decimalPos < 0) {
+ decimalPos = digitCount;
+ }
+ break;
+ case ',':
+ if (digitCount > 0 && decimalPos < 0) {
+ if (thousandPos >= 0) {
+ if (thousandPos == digitCount) {
+ thousandCount++;
+ break;
+ }
+ thousandSeps = 1;
+ }
+ thousandPos = digitCount;
+ thousandCount = 1;
+ }
+ break;
+ case '%':
+ percent++;
+ scaleAdjust += 2;
+ break;
+ case 0x2030:
+ permille++;
+ scaleAdjust += 3;
+ break;
+ case '\'':
+ case '"':
+ while (*src != 0 && *src++ != ch);
+ break;
+ case '\\':
+ if (*src != 0) src++;
+ break;
+ case 'E':
+ case 'e':
+ if (*src=='0' || ((*src == '+' || *src == '-') && src[1] == '0')) {
+ while (*++src == '0');
+ scientific = 1;
+ }
+ break;
+ }
+ }
+
+ if (decimalPos < 0) decimalPos = digitCount;
+ if (thousandPos >= 0) {
+ if (thousandPos == decimalPos) {
+ scaleAdjust -= thousandCount * 3;
+ }
+ else {
+ thousandSeps = 1;
+ }
+ }
+ if ((GetDigitsBuffer(number))[0] != 0) {
+ number->scale += scaleAdjust;
+ int pos = scientific? digitCount: number->scale + digitCount - decimalPos;
+ RoundNumber(number, pos);
+ if ((GetDigitsBuffer(number))[0] == 0) {
+ src = FindSection(format, 2);
+ if (src != section) {
+ section = src;
+ goto ParseSection;
+ }
+ }
+ } else {
+ number->sign = 0; // We need to format -0 without the sign set.
+ number->scale = 0; // Decimals with scale ('0.00') should be rounded.
+ }
+
+ firstDigit = firstDigit < decimalPos? decimalPos - firstDigit: 0;
+ lastDigit = lastDigit > decimalPos? decimalPos - lastDigit: 0;
+ if (scientific) {
+ digPos = decimalPos;
+ adjust = 0;
+ }
+ else {
+ digPos = number->scale > decimalPos? number->scale: decimalPos;
+ adjust = number->scale - decimalPos;
+ }
+ src = section;
+ dig = GetDigitsBuffer(number);
+
+ // Find maximum number of characters that the destination string can grow by
+ // in the following while loop. Use this to avoid buffer overflows.
+ // Longest strings are potentially +/- signs with 10 digit exponents,
+ // or decimal numbers, or the while loops copying from a quote or a \ onwards.
+ // Check for positive and negative
+ UINT64 maxStrIncLen = 0; // We need this to be UINT64 since the percent computation could go beyond a UINT.
+ if (number->sign) {
+ maxStrIncLen = sNegative->GetStringLength();
+ }
+ else {
+ maxStrIncLen = sPositive->GetStringLength();
+ }
+
+ // Add for any big decimal seperator
+ maxStrIncLen += sNumberDecimal->GetStringLength();
+
+ // Add for scientific
+ if (scientific) {
+ int inc1 = sPositive->GetStringLength();
+ int inc2 = sNegative->GetStringLength();
+ maxStrIncLen +=(inc1>inc2)?inc1:inc2;
+ }
+
+ // Add for percent separator
+ if (percent) {
+ maxStrIncLen += ((INT64)sPercent->GetStringLength()) * percent;
+ }
+
+ // Add for permilli separator
+ if (permille) {
+ maxStrIncLen += ((INT64)sPerMille->GetStringLength()) * permille;
+ }
+
+ //adjust can be negative, so we make this an int instead of an unsigned int.
+ // adjust represents the number of characters over the formatting eg. format string is "0000" and you are trying to
+ // format 100000 (6 digits). Means adjust will be 2. On the other hand if you are trying to format 10 adjust will be
+ // -2 and we'll need to fixup these digits with 0 padding if we have 0 formatting as in this example.
+ INT64 adjustLen=(adjust>0)?adjust:0; // We need to add space for these extra characters anyway.
+ CQuickBytes thousands;
+ INT32 bufferLen2 = 125;
+ INT32 *thousandsSepPos = NULL;
+ INT32 thousandsSepCtr = -1;
+
+ if (thousandSeps) { // Fixup possible buffer overrun problems
+ // We need to precompute this outside the number formatting loop
+ if(cNumberGroup->GetNumComponents() == 0) {
+ thousandSeps = 0; // Nothing to add
+ }
+ else {
+ thousandsSepPos = (INT32 *)thousands.AllocThrows(bufferLen2 * sizeof(INT32));
+ // We need this array to figure out where to insert the thousands separator. We would have to traverse the string
+ // backwards. PIC formatting always traverses forwards. These indices are precomputed to tell us where to insert
+ // the thousands separator so we can get away with traversing forwards. Note we only have to compute up to digPos.
+ // The max is not bound since you can have formatting strings of the form "000,000..", and this
+ // should handle that case too.
+
+ const I4* groupDigits = cNumberGroup->GetDirectConstPointerToNonObjectElements();
+ _ASSERTE(groupDigits != NULL);
+
+ int groupSizeIndex = 0; // index into the groupDigits array.
+ INT64 groupTotalSizeCount = 0;
+ int groupSizeLen = cNumberGroup->GetNumComponents(); // the length of groupDigits array.
+ if (groupSizeLen != 0)
+ groupTotalSizeCount = groupDigits[groupSizeIndex]; // the current running total of group size.
+ int groupSize = static_cast<INT32>(groupTotalSizeCount); // safe cast as long as groupDigits remains I4
+
+ int totalDigits = digPos + ((adjust < 0)?adjust:0); // actual number of digits in o/p
+ int numDigits = (firstDigit > totalDigits) ? firstDigit : totalDigits;
+ while (numDigits > groupTotalSizeCount) {
+ if (groupSize == 0)
+ break;
+ thousandsSepPos[++thousandsSepCtr] = static_cast<INT32>(groupTotalSizeCount);
+ if (groupSizeIndex < groupSizeLen - 1) {
+ groupSizeIndex++;
+ groupSize = groupDigits[groupSizeIndex];
+ }
+ groupTotalSizeCount += groupSize;
+ if (bufferLen2 - thousandsSepCtr < 10) { // Slack of 10
+ bufferLen2 *= 2;
+ thousands.ReSizeThrows(bufferLen2*sizeof(INT32)); // memcopied by CQuickBytes automatically
+ thousandsSepPos = (INT32 *)thousands.Ptr();
+ }
+ }
+
+ // We already have computed the number of separators above. Simply add space for them.
+ adjustLen += ( (thousandsSepCtr + 1) * ((INT64)sNumberGroup->GetStringLength()));
+ }
+ }
+
+ maxStrIncLen += adjustLen;
+
+ // Allocate temp buffer - gotta deal with Schertz' 500 MB strings.
+ // Some computations like when you specify Int32.MaxValue-2 %'s and each percent is setup to be Int32.MaxValue in length
+ // will generate a result that will be largest than an unsigned int can hold. This is to protect against overflow.
+ UINT64 tempLen = str->GetStringLength() + maxStrIncLen + 10; // Include a healthy amount of temp space.
+ if (tempLen > 0x7FFFFFFF)
+ COMPlusThrowOM(); // if we overflow
+
+ unsigned int bufferLen = (UINT)tempLen;
+ if (bufferLen < 250) // Stay under 512 bytes
+ bufferLen = 250; // This is to prevent unnecessary calls to resize
+ buffer = (wchar *) buf.AllocThrows(bufferLen* sizeof(WCHAR));
+ dst = buffer;
+
+
+ if (number->sign && section == format) {
+ AddStringRef(&dst, sNegative);
+ }
+
+ BOOL decimalWritten = FALSE;
+
+ while ((ch = *src++) != 0 && ch != ';') {
+ // Make sure temp buffer is big enough, else resize it.
+ if (bufferLen - (unsigned int)(dst-buffer) < 10) {
+ int offset = static_cast<INT32>(dst - buffer);
+ bufferLen *= 2;
+ buf.ReSizeThrows(bufferLen*sizeof(WCHAR));
+ buffer = (wchar*)buf.Ptr(); // memcopied by QuickBytes automatically
+ dst = buffer + offset;
+ }
+
+ if (adjust > 0) {
+ switch (ch) {
+ case '#':
+ case '0':
+ case '.':
+ while (adjust > 0) { // digPos will be one greater than thousandsSepPos[thousandsSepCtr] since we are at
+ // the character after which the groupSeparator needs to be appended.
+ *dst++ = *dig != 0? *dig++: '0';
+ if (thousandSeps && digPos > 1 && thousandsSepCtr>=0) {
+ if (digPos == thousandsSepPos[thousandsSepCtr] + 1) {
+ AddStringRef(&dst, sNumberGroup);
+ thousandsSepCtr--;
+ }
+ }
+ digPos--;
+ adjust--;
+ }
+ }
+ }
+
+ switch (ch) {
+ case '#':
+ case '0':
+ {
+ if (adjust < 0) {
+ adjust++;
+ ch = digPos <= firstDigit? '0': 0;
+ }
+ else {
+ ch = *dig != 0? *dig++: digPos > lastDigit? '0': 0;
+ }
+ if (ch != 0) {
+ *dst++ = ch;
+ if (thousandSeps && digPos > 1 && thousandsSepCtr>=0) {
+ if (digPos == thousandsSepPos[thousandsSepCtr] + 1) {
+ AddStringRef(&dst, sNumberGroup);
+ thousandsSepCtr--;
+ }
+ }
+ }
+
+ digPos--;
+ break;
+ }
+ case '.':
+ {
+ if (digPos != 0 || decimalWritten) {
+ // For compatibility, don't echo repeated decimals
+ break;
+ }
+ // If the format has trailing zeros or the format has a decimal and digits remain
+ if (lastDigit < 0
+ || (decimalPos < digitCount && *dig != 0)) {
+ AddStringRef(&dst, sNumberDecimal);
+ decimalWritten = TRUE;
+ }
+ break;
+ }
+ case 0x2030:
+ AddStringRef(&dst, sPerMille);
+ break;
+ case '%':
+ AddStringRef(&dst, sPercent);
+ break;
+ case ',':
+ break;
+ case '\'':
+ case '"':
+ // Buffer overflow possibility
+ while (*src != 0 && *src != ch) {
+ *dst++ = *src++;
+ if ((unsigned int)(dst-buffer) == bufferLen-1) {
+ if (bufferLen - (unsigned int)(dst-buffer) < maxStrIncLen) {
+ int offset = static_cast<INT32>(dst - buffer);
+ bufferLen *= 2;
+ buf.ReSizeThrows(bufferLen*sizeof(WCHAR)); // memcopied by CQuickBytes automatically
+ buffer = (wchar *)buf.Ptr();
+ dst = buffer + offset;
+ }
+ }
+ }
+ if (*src != 0) src++;
+ break;
+ case '\\':
+ if (*src != 0) *dst++ = *src++;
+ break;
+ case 'E':
+ case 'e':
+ {
+ STRINGREF sign = NULL;
+ int i = 0;
+ if (scientific) {
+ if (*src=='0') {
+ //Handles E0, which should format the same as E-0
+ i++;
+ } else if (*src == '+' && src[1] == '0') {
+ //Handles E+0
+ sign = sPositive;
+ } else if (*src == '-' && src[1] == '0') {
+ //Handles E-0
+ //Do nothing, this is just a place holder s.t. we don't break out of the loop.
+ } else {
+ *dst++ = ch;
+ break;
+ }
+ while (*++src == '0') i++;
+ if (i > 10) i = 10;
+ int exp = (GetDigitsBuffer(number))[0] == 0 ? 0 : number->scale - decimalPos;
+ dst = FormatExponent(dst, exp, ch, sign, sNegative, i);
+ scientific = 0;
+ }
+ else
+ {
+ *dst++ = ch; // Copy E or e to output
+ if (*src== '+' || *src == '-') {
+ *dst++ = *src++;
+ }
+ while (*src == '0') {
+ *dst++ = *src++;
+ }
+ }
+ break;
+ }
+ default:
+ *dst++ = ch;
+ }
+ }
+ if (!((dst - buffer >= 0) && (dst - buffer <= (int)bufferLen))) {
+ DoJITFailFast();
+ }
+ STRINGREF newStr = StringObject::NewString(buffer, (int)(dst - buffer));
+ return newStr;
+}
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+FCIMPL3_VII(Object*, COMNumber::FormatDecimal, FC_DECIMAL value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ NUMBER number;
+
+ wchar fmt;
+ int digits;
+
+ STRINGREF refRetVal = NULL;
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
+
+ struct _gc
+ {
+ STRINGREF format;
+ NUMFMTREF numfmt;
+ } gc;
+
+ gc.format = (STRINGREF) formatUNSAFE;
+ gc.numfmt = (NUMFMTREF) numfmtUNSAFE;
+
+ if (gc.numfmt == 0)
+ COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+
+ COMDecimal::DecimalToNumber(&value, &number);
+
+ fmt = ParseFormatSpecifier(gc.format, &digits);
+ if (fmt != 0) {
+ refRetVal = NumberToString(&number, fmt, digits, gc.numfmt, TRUE);
+ } else {
+ refRetVal = NumberToStringFormat(&number, gc.format, gc.numfmt);
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+FCIMPL3_VII(Object*, COMNumber::FormatDouble, double value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ NUMBER number;
+ int digits;
+ double dTest;
+
+ struct _gc
+ {
+ STRINGREF format;
+ NUMFMTREF numfmt;
+ STRINGREF refRetVal;
+ } gc;
+
+ gc.format = (STRINGREF) formatUNSAFE;
+ gc.numfmt = (NUMFMTREF) numfmtUNSAFE;
+ gc.refRetVal = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ if (gc.numfmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ wchar fmt = ParseFormatSpecifier(gc.format, &digits);
+ wchar val = (fmt & 0xFFDF);
+ int precision = DOUBLE_PRECISION;
+ switch (val) {
+ case 'R':
+ //In order to give numbers that are both friendly to display and round-trippable,
+ //we parse the number using 15 digits and then determine if it round trips to the same
+ //value. If it does, we convert that NUMBER to a string, otherwise we reparse using 17 digits
+ //and display that.
+
+ DoubleToNumber(value, DOUBLE_PRECISION, &number);
+
+ if (number.scale == (int) SCALE_NAN) {
+ gc.refRetVal = gc.numfmt->sNaN;
+ goto lExit;
+ }
+
+ if (number.scale == SCALE_INF) {
+ gc.refRetVal = (number.sign? gc.numfmt->sNegativeInfinity: gc.numfmt->sPositiveInfinity);
+ goto lExit;
+ }
+
+ NumberToDouble(&number, &dTest);
+
+ if (dTest == value) {
+ gc.refRetVal = NumberToString(&number, 'G', DOUBLE_PRECISION, gc.numfmt);
+ goto lExit;
+ }
+
+ DoubleToNumber(value, 17, &number);
+ gc.refRetVal = NumberToString(&number, 'G', 17, gc.numfmt);
+ goto lExit;
+ break;
+
+ case 'E':
+ // Here we round values less than E14 to 15 digits
+ if (digits > 14) {
+ precision = 17;
+ }
+ break;
+
+ case 'G':
+ // Here we round values less than G15 to 15 digits, G16 and G17 will not be touched
+ if (digits > 15) {
+ precision = 17;
+ }
+ break;
+
+ }
+
+ DoubleToNumber(value, precision, &number);
+
+ if (number.scale == (int) SCALE_NAN) {
+ gc.refRetVal = gc.numfmt->sNaN;
+ goto lExit;
+ }
+
+ if (number.scale == SCALE_INF) {
+ gc.refRetVal = (number.sign? gc.numfmt->sNegativeInfinity: gc.numfmt->sPositiveInfinity);
+ goto lExit;
+ }
+
+ if (fmt != 0) {
+ gc.refRetVal = NumberToString( &number, fmt, digits, gc.numfmt);
+ }
+ else {
+ gc.refRetVal = NumberToStringFormat( &number, gc.format, gc.numfmt);
+ }
+
+lExit: ;
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetVal);
+}
+FCIMPLEND
+
+//
+//This function and the function pointer which we use to access are
+//to prevent VC7 from optimizing away our cast from double to float.
+//We need this narrowing operation to verify whether or not we successfully round-tripped
+//the single value.
+
+//
+// We need this method to have volatile arguments.
+//
+static void CvtToFloat(double val, RAW_KEYWORD(volatile) float* fltPtr)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ *fltPtr = (float)val;
+}
+
+void (*CvtToFloatPtr)(double val, RAW_KEYWORD(volatile) float* fltPtr) = CvtToFloat;
+
+
+FCIMPL3_VII(Object*, COMNumber::FormatSingle, float value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ NUMBER number;
+ int digits;
+ double dTest;
+ double argsValue = value;
+
+ struct _gc
+ {
+ STRINGREF format;
+ NUMFMTREF numfmt;
+ STRINGREF refRetVal;
+ } gc;
+
+ gc.format = (STRINGREF) formatUNSAFE;
+ gc.numfmt = (NUMFMTREF) numfmtUNSAFE;
+ gc.refRetVal = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ if (gc.numfmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ wchar fmt = ParseFormatSpecifier(gc.format, &digits);
+ wchar val = fmt & 0xFFDF;
+ int precision = FLOAT_PRECISION;
+ switch (val) {
+ case 'R':
+ {
+ //In order to give numbers that are both friendly to display and round-trippable,
+ //we parse the number using 7 digits and then determine if it round trips to the same
+ //value. If it does, we convert that NUMBER to a string, otherwise we reparse using 9 digits
+ //and display that.
+
+ DoubleToNumber(argsValue, FLOAT_PRECISION, &number);
+#ifndef FEATURE_BCL_FORMATTING
+ // Make sure that BCL formatting is used for Single to avoid lossy conversion to Double
+ number.palNumber = 0;
+#endif
+
+ if (number.scale == (int) SCALE_NAN) {
+ gc.refRetVal = gc.numfmt->sNaN;
+ goto lExit;
+ }
+ if (number.scale == SCALE_INF) {
+ gc.refRetVal = (number.sign? gc.numfmt->sNegativeInfinity: gc.numfmt->sPositiveInfinity);
+ goto lExit;
+ }
+
+ NumberToDouble(&number, &dTest);
+
+ Volatile<float> fTest;
+
+ (*CvtToFloatPtr)(dTest, &fTest);
+
+ if (fTest == value) {
+ gc.refRetVal = NumberToString(&number, 'G', FLOAT_PRECISION, gc.numfmt);
+ goto lExit;
+ }
+
+ DoubleToNumber(argsValue, 9, &number);
+ gc.refRetVal = NumberToString(&number, 'G', 9, gc.numfmt);
+ goto lExit;
+ }
+ break;
+ case 'E':
+ // Here we round values less than E14 to 15 digits
+ if (digits > 6) {
+ precision = 9;
+ }
+ break;
+
+
+ case 'G':
+ // Here we round values less than G15 to 15 digits, G16 and G17 will not be touched
+ if (digits > 7) {
+ precision = 9;
+ }
+ break;
+ }
+
+ DoubleToNumber(value, precision, &number);
+#ifndef FEATURE_BCL_FORMATTING
+ // Make sure that BCL formatting is used for Single to avoid lossy conversion to Double
+ number.palNumber = 0;
+#endif
+
+ if (number.scale == (int) SCALE_NAN) {
+ gc.refRetVal = gc.numfmt->sNaN;
+ goto lExit;
+ }
+
+ if (number.scale == SCALE_INF) {
+ gc.refRetVal = (number.sign? gc.numfmt->sNegativeInfinity: gc.numfmt->sPositiveInfinity);
+ goto lExit;
+ }
+
+ if (fmt != 0) {
+ gc.refRetVal = NumberToString( &number, fmt, digits, gc.numfmt);
+ }
+ else {
+ gc.refRetVal = NumberToStringFormat( &number, gc.format, gc.numfmt);
+ }
+
+lExit: ;
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetVal);
+}
+FCIMPLEND
+
+FCIMPL3(Object*, COMNumber::FormatInt32, INT32 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = (STRINGREF)formatUNSAFE;
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+
+ //ANDing fmt with FFDF has the effect of uppercasing the character because
+ //we've removed the bit that marks lower-case.
+ switch (fmt & 0xFFDF) {
+ case 'G':
+ if (digits > 0)
+ {
+ NUMBER number;
+ Int32ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ // fall through
+ case 'D':
+ gc.refRetString = Int32ToDecStr(value, digits, gc.refNumFmt->sNegative);
+ break;
+ case 'X':
+ //The fmt-(X-A+10) has the effect of dictating whether we produce uppercase
+ //or lowercase hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x'
+ //as the format code produces lowercase.
+ gc.refRetString = Int32ToHexStr(value, fmt - ('X' - 'A' + 10), digits);
+ break;
+ default:
+ NUMBER number;
+ Int32ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+FCIMPL3(Object*, COMNumber::FormatUInt32, UINT32 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = (STRINGREF)formatUNSAFE;
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+ switch (fmt & 0xFFDF)
+ {
+ case 'G':
+ if (digits > 0)
+ {
+ NUMBER number;
+ UInt32ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ // fall through
+ case 'D':
+ gc.refRetString = UInt32ToDecStr(value, digits);
+ break;
+ case 'X':
+ gc.refRetString = Int32ToHexStr(value, fmt - ('X' - 'A' + 10), digits);
+ break;
+ default:
+ NUMBER number;
+ UInt32ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+FCIMPL3_VII(Object*, COMNumber::FormatInt64, INT64 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = ObjectToSTRINGREF(formatUNSAFE);
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+ switch (fmt & 0xFFDF)
+ {
+ case 'G':
+ if (digits > 0)
+ {
+ NUMBER number;
+ Int64ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ // fall through
+ case 'D':
+ gc.refRetString = Int64ToDecStr(value, digits, gc.refNumFmt->sNegative);
+ break;
+ case 'X':
+ gc.refRetString = Int64ToHexStr(value, fmt - ('X' - 'A' + 10), digits);
+ break;
+ default:
+ NUMBER number;
+ Int64ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+FCIMPL3_VII(Object*, COMNumber::FormatUInt64, UINT64 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = ObjectToSTRINGREF(formatUNSAFE);
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+ switch (fmt & 0xFFDF) {
+ case 'G':
+ if (digits > 0)
+ {
+ NUMBER number;
+ UInt64ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ // fall through
+ case 'D':
+ gc.refRetString = UInt64ToDecStr(value, digits);
+ break;
+ case 'X':
+ gc.refRetString = Int64ToHexStr(value, fmt - ('X' - 'A' + 10), digits);
+ break;
+ default:
+ NUMBER number;
+ UInt64ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+#if !defined(FEATURE_CORECLR)
+//
+// Used by base types that are not in mscorlib.dll (such as System.Numerics.BigInteger in System.Core.dll)
+// Note that the allDigits buffer must be fixed across this call or you will introduce a GC Hole.
+//
+FCIMPL4(Object*, COMNumber::FormatNumberBuffer, BYTE* number, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE, __in_z wchar_t* allDigits)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+ NUMBER* pNumber;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = ObjectToSTRINGREF(formatUNSAFE);
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+
+ pNumber = (NUMBER*) number;
+
+ pNumber->allDigits = allDigits;
+
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(pNumber, fmt, digits, gc.refNumFmt);
+ }
+ else {
+ gc.refRetString = NumberToStringFormat(pNumber, gc.refFormat, gc.refNumFmt);
+ }
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+#endif // !FEATURE_CORECLR
+
+FCIMPL2(FC_BOOL_RET, COMNumber::NumberBufferToDecimal, BYTE* number, DECIMAL* value)
+{
+ FCALL_CONTRACT;
+
+ FC_RETURN_BOOL(COMDecimal::NumberToDecimal((NUMBER *) number, value) != 0);
+}
+FCIMPLEND
+
+FCIMPL2(FC_BOOL_RET, COMNumber::NumberBufferToDouble, BYTE* number, double* value)
+{
+ FCALL_CONTRACT;
+
+ 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) {
+ FC_RETURN_BOOL(false);
+ }
+ if (e == 0 && fmntLow ==0 && fmntHigh == 0) {
+ d = 0;
+ }
+ *value = d;
+ FC_RETURN_BOOL(true);
+}
+FCIMPLEND