X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fdecimal.c;h=3bbd0058032c2534bb43ef4161c7faad6e2f4870;hb=c2022b34b7820821406d4e46df7868db89144f2c;hp=626a8d91e3d27f00d2223d3df711b4b663b458ef;hpb=1c14f1ee6d701510c15499d71fc2b324151a8629;p=mono.git diff --git a/mono/metadata/decimal.c b/mono/metadata/decimal.c index 626a8d91e3d..3bbd0058032 100644 --- a/mono/metadata/decimal.c +++ b/mono/metadata/decimal.c @@ -1,12 +1,12 @@ /* - decimal.c - - conversions and numerical operations for the c# type System.Decimal - - Author: Martin Weindel (martin.weindel@t-online.de) - - (C) 2001 by Martin Weindel -*/ + * decimal.c + * + * conversions and numerical operations for the c# type System.Decimal + * + * Author: Martin Weindel (martin.weindel@t-online.de) + * + * (C) 2001 by Martin Weindel + */ /* * machine dependent configuration for @@ -16,10 +16,15 @@ #include "config.h" #include #include -#include #include #include #include +#ifdef HAVE_MEMORY_H +#include +#endif +#ifdef _MSC_VER +#include +#endif #ifndef DISABLE_DECIMAL @@ -398,8 +403,8 @@ DECINLINE static void div192by32(guint64* plo, guint64* pmi, guint64* phi, /* returns upper 32bit for a(192bit) /= b(32bit) a will contain remainder */ -static guint32 div192by96to32withRest(guint64* palo, guint64* pami, guint64* pahi, - guint32 blo, guint32 bmi, guint32 bhi) +DECINLINE static guint32 div192by96to32withRest(guint64* palo, guint64* pami, guint64* pahi, + guint32 blo, guint32 bmi, guint32 bhi) { guint64 rlo, rmi, rhi; /* remainder */ guint64 tlo, thi; /* term */ @@ -426,9 +431,9 @@ static guint32 div192by96to32withRest(guint64* palo, guint64* pami, guint64* pah /* c(128bit) = a(192bit) / b(96bit) b must be >= 2^95 */ -static void div192by96to128(guint64 alo, guint64 ami, guint64 ahi, - guint32 blo, guint32 bmi, guint32 bhi, - guint64* pclo, guint64* pchi) +DECINLINE static void div192by96to128(guint64 alo, guint64 ami, guint64 ahi, + guint32 blo, guint32 bmi, guint32 bhi, + guint64* pclo, guint64* pchi) { guint64 rlo, rmi, rhi; /* remainder */ guint32 h, c; @@ -462,8 +467,8 @@ DECINLINE static void roundUp128(guint64* pclo, guint64* pchi) { if (++(*pclo) == 0) ++(*pchi); } -static int normalize128(guint64* pclo, guint64* pchi, int* pScale, - int roundFlag, int roundBit) +DECINLINE static int normalize128(guint64* pclo, guint64* pchi, int* pScale, + int roundFlag, int roundBit) { guint32 overhang = (guint32)(*pchi >> 32); int scale = *pScale; @@ -518,101 +523,108 @@ DECINLINE static int maxLeftShift(/*[In, Out]*/decimal_repr* pA) DECINLINE static void rshift128(guint64* pclo, guint64* pchi) { *pclo >>= 1; - if (*pchi & 1) *pclo |= LIT_GUINT64_HIGHBIT; + *pclo |= (*pchi & 1) << 63; *pchi >>= 1; } DECINLINE static void lshift96(guint32* pclo, guint32* pcmid, guint32* pchi) { *pchi <<= 1; - if (*pcmid & LIT_GUINT32_HIGHBIT) (*pchi)++; + *pchi |= (*pcmid & LIT_GUINT32_HIGHBIT) >> 31; *pcmid <<= 1; - if (*pclo & LIT_GUINT32_HIGHBIT) (*pcmid)++; + *pcmid |= (*pclo & LIT_GUINT32_HIGHBIT) >> 31; *pclo <<= 1; } DECINLINE static void lshift128(guint64* pclo, guint64* pchi) { *pchi <<= 1; - if (*pclo & LIT_GUINT64_HIGHBIT) (*pchi)++; + *pchi |= (*pclo & LIT_GUINT64_HIGHBIT) >> 63; *pclo <<= 1; } DECINLINE static void rshift192(guint64* pclo, guint64* pcmi, guint64* pchi) { *pclo >>= 1; - if (*pcmi & 1) *pclo |= LIT_GUINT64_HIGHBIT; + *pclo |= (*pcmi & 1) << 63; *pcmi >>= 1; - if (*pchi & 1) *pcmi |= LIT_GUINT64_HIGHBIT; + *pcmi |= (*pchi & 1) << 63; *pchi >>= 1; } +#if defined(__native_client__) && (defined(__i386__) || defined(__x86_64)) +#define USE_X86_32BIT_INSTRUCTIONS 1 +#endif + +static inline gint +my_g_bit_nth_msf (gsize mask) +{ + /* Mask is expected to be != 0 */ +#if (defined(__i386__) && defined(__GNUC__)) || defined(USE_X86_32BIT_INSTRUCTIONS) + int r; + + __asm__("bsrl %1,%0\n\t" + : "=r" (r) : "rm" (mask)); + return r; +#elif defined(__x86_64) && defined(__GNUC__) + guint64 r; + + __asm__("bsrq %1,%0\n\t" + : "=r" (r) : "rm" (mask)); + return r; +#elif defined(__i386__) && defined(_MSC_VER) + unsigned long bIndex = 0; + if (_BitScanReverse (&bIndex, mask)) + return bIndex; + return -1; +#elif defined(_M_X64) && defined(_MSC_VER) + unsigned long bIndex = 0; + if (_BitScanReverse64 (&bIndex, mask)) + return bIndex; + return -1; +#elif defined(__s390x__) && defined(__NOT_YET) + guint64 r; + + __asm__("\tlrvgr\t%1,%1\n" + "\tflogr\t%0,%1\n" + "\tjz\t0f\n" + "\tlghi\t%0,-1\n" + "0:\n" + : "=r" (r) : "r" (mask) : "cc"); +#else + int i; + + i = sizeof (gsize) * 8; + while (i > 0) { + i --; + if (mask & (1ULL << i)) + return i; + } + return -1; +#endif +} + /* returns log2(a) or DECIMAL_LOG_NEGINF for a = 0 */ DECINLINE static int log2_32(guint32 a) { - int tlog2 = 0; - if (a == 0) return DECIMAL_LOG_NEGINF; - if ((a >> 16) != 0) { - a >>= 16; - tlog2 += 16; - } - if ((a >> 8) != 0) { - a >>= 8; - tlog2 += 8; - } - if ((a >> 4) != 0) { - a >>= 4; - tlog2 += 4; - } - if ((a >> 2) != 0) { - a >>= 2; - tlog2 += 2; - } - if ((a >> 1) != 0) { - a >>= 1; - tlog2 += 1; - } - tlog2 += (int) a; - - return tlog2; + return my_g_bit_nth_msf (a) + 1; } /* returns log2(a) or DECIMAL_LOG_NEGINF for a = 0 */ DECINLINE static int log2_64(guint64 a) { - int tlog2 = 0; - if (a == 0) return DECIMAL_LOG_NEGINF; - if ((a >> 32) != 0) { - a >>= 32; - tlog2 += 32; - } - if ((a >> 16) != 0) { - a >>= 16; - tlog2 += 16; - } - if ((a >> 8) != 0) { - a >>= 8; - tlog2 += 8; - } - if ((a >> 4) != 0) { - a >>= 4; - tlog2 += 4; - } - if ((a >> 2) != 0) { - a >>= 2; - tlog2 += 2; - } - if ((a >> 1) != 0) { - a >>= 1; - tlog2 += 1; - } - tlog2 += (int) a; - - return tlog2; +#if SIZEOF_VOID_P == 8 + return my_g_bit_nth_msf (a) + 1; +#else + if ((a >> 32) == 0) + return my_g_bit_nth_msf ((guint32)a) + 1; + else + return my_g_bit_nth_msf ((guint32)(a >> 32)) + 1 + 32; +#endif } /* returns log2(a) or DECIMAL_LOG_NEGINF for a = 0 */ @@ -692,11 +704,38 @@ DECINLINE static int rescale128(guint64* pclo, guint64* pchi, int* pScale, int t /* reduce exp */ while (texp > 0 && scale <= maxScale) { overhang = (guint32)(*pchi >> 32); - while (texp > 0 && ((*pclo & 1) == 0 || overhang > (2< 0 && (overhang > (2<> 32); } + */ + if (overhang > 0) { + int msf = my_g_bit_nth_msf (overhang); + int shift = msf - (DECIMAL_MAX_INTFACTORS + 2); + + if (shift >= texp) + shift = texp - 1; + + if (shift > 0) { + texp -= shift; + *pclo = (*pclo >> shift) | ((*pchi & ((1 << shift) - 1)) << (64 - shift)); + *pchi >>= shift; + overhang >>= shift; + + g_assert (texp > 0); + g_assert (overhang > (2 << DECIMAL_MAX_INTFACTORS)); + } + } + while (texp > 0 && (overhang > (2<>= 1; + } if (texp > DECIMAL_MAX_INTFACTORS) i = DECIMAL_MAX_INTFACTORS; else i = texp; @@ -741,6 +780,34 @@ DECINLINE static int rescale128(guint64* pclo, guint64* pchi, int* pScale, int t return normalize128(pclo, pchi, pScale, roundFlag, roundBit); } +guint32 rest; +static void trimExcessScale(guint64* pclo, guint64* pchi, int* pScale) +{ + guint64 ilo = *pclo, lastlo; + guint64 ihi = *pchi, lasthi; + int scale = *pScale; + int i = 0, roundBit; + + while (scale > 0) { + scale--; + i++; + lastlo = ilo; + lasthi = ihi; + + roundBit = div128by32(&ilo, &ihi, 10, &rest); + if (rest != 0){ + i--; + if (i == 0) + return; + + *pclo = lastlo; + *pchi = lasthi; + *pScale = scale+1; + return; + } + } +} + /* performs a += b */ gint32 mono_decimalIncr(/*[In, Out]*/decimal_repr* pA, /*[In]*/decimal_repr* pB) { @@ -777,7 +844,7 @@ gint32 mono_decimalIncr(/*[In, Out]*/decimal_repr* pA, /*[In]*/decimal_repr* pB) /* Estimate log10 and scale of result for adjusting scales */ log2A = log2withScale_128(alo, ahi, scaleA); log2B = log2withScale_128(blo, bhi, scaleB); - log2Result = (log2A >= log2B) ? log2A : log2B; + log2Result = MAX (log2A, log2B); if (!subFlag) log2Result++; /* result can have one bit more */ log10Result = (log2Result * 1000) / 3322 + 1; /* we will calculate in 128bit, so we may need to adjust scale */ @@ -882,7 +949,11 @@ gint32 mono_double2decimal(/*[Out]*/decimal_repr* pA, double val, gint32 digits) PRECONDITION(digits <= 15); sign = ((*p & LIT_GUINT64_HIGHBIT) != 0) ? 1 : 0; + + // Exponent k = ((guint16)((*p) >> 52)) & 0x7FF; + + // 1-bit followed by the fraction component from the float alo = (*p & LIT_GUINT64(0xFFFFFFFFFFFFF)) | LIT_GUINT64(0x10000000000000); ahi = 0; @@ -901,7 +972,7 @@ gint32 mono_double2decimal(/*[Out]*/decimal_repr* pA, double val, gint32 digits) } scale = 0; - rc = rescale128(&alo, &ahi, &scale, -texp, 0, DECIMAL_MAX_SCALE, 0); + rc = rescale128(&alo, &ahi, &scale, -texp, 0, DECIMAL_MAX_SCALE, 1); if (rc != DECIMAL_SUCCESS) return rc; sigDigits = calcDigits(alo, ahi); @@ -922,6 +993,14 @@ gint32 mono_double2decimal(/*[Out]*/decimal_repr* pA, double val, gint32 digits) } } + // + // Turn the double 0.6 which at this point is: + // 0.6000000000000000 + // into: + // 0.6 + // + trimExcessScale (&alo, &ahi, &scale); + return pack128toDecimal(pA, alo, ahi, scale, sign); } @@ -1004,6 +1083,10 @@ gint32 mono_string2decimal(/*[Out]*/decimal_repr* pA, MonoString* str, gint32 de } } + // Set correct scale for zeros decimal (000 input is 0.00) + if (sigLen < 0 && len > decrDecimal) + sigLen = len; + scale = sigLen - decrDecimal; if (i < len) { /* too much digits, we must round */ @@ -1029,131 +1112,13 @@ gint32 mono_string2decimal(/*[Out]*/decimal_repr* pA, MonoString* str, gint32 de if (rc != DECIMAL_SUCCESS) return rc; } - if (alo == 0 && ahi == 0) { - DECINIT(pA); + if (alo == 0 && ahi == 0 && scale <= 0) { return DECIMAL_SUCCESS; } else { return pack128toDecimal(pA, alo, ahi, sigLen - decrDecimal, sign); } } -/** - * mono_decimal2string: - * @ - * returns minimal number of digit string to represent decimal - * No leading or trailing zeros ! - * Examples: - * *pA == 0 => buf = "", *pDecPos = 1, *pSign = 0 - * *pA == 12.34 => buf = "1234", *pDecPos = 2, *pSign = 0 - * *pA == -1000.0000 => buf = "1", *pDecPos = 4, *pSign = 1 - * *pA == -0.00000076 => buf = "76", *pDecPos = -6, *pSign = 0 - * - * Parameters: - * pA decimal instance to convert - * digits < 0: use decimals instead - * = 0: gets mantisse as integer - * > 0: gets at most digits, rounded according to banker's rule if necessary - * decimals only used if digits < 0 - * >= 0: number of decimal places - * buf pointer to result buffer - * bufSize size of buffer - * pDecPos receives insert position of decimal point relative to start of buffer - * pSign receives sign - */ -gint32 mono_decimal2string(/*[In]*/decimal_repr* pA, gint32 digits, gint32 decimals, - MonoArray* pArray, gint32 bufSize, gint32* pDecPos, gint32* pSign) -{ - guint16 tmp[41]; - guint16 *buf = (guint16*) mono_array_addr(pArray, guint16, 0); - guint16 *q, *p = tmp; - decimal_repr aa; - guint64 alo, ahi; - guint32 rest; - gint32 sigDigits, d; - int i, scale, len; - - MONO_ARCH_SAVE_REGS; - - scale = pA->signscale.scale; - DECTO128(pA, alo, ahi); - sigDigits = calcDigits(alo, ahi); /* significant digits */ - - /* calc needed digits (without leading or trailing zeros) */ - d = (digits == 0) ? sigDigits : digits; - if (d < 0) { /* use decimals ? */ - if (0 <= decimals && decimals < scale) { - d = sigDigits - scale + decimals; - } else { - d = sigDigits; /* use all you can get */ - } - } - - if (sigDigits > d) { /* we need to round decimal number */ - DECCOPY(&aa, pA); - aa.signscale.scale = DECIMAL_MAX_SCALE; - mono_decimalRound(&aa, DECIMAL_MAX_SCALE - sigDigits + d); - DECTO128(&aa, alo, ahi); - sigDigits += calcDigits(alo, ahi) - d; - } - - len = 0; - if (d > 0) { - /* get digits starting from the tail */ - for (; (alo != 0 || ahi != 0) && len < 40; len++) { - div128by32(&alo, &ahi, 10, &rest); - *p++ = '0' + (char) rest; - } - } - *p = 0; - - if (len >= bufSize) return DECIMAL_BUFFER_OVERFLOW; - - /* now we have the minimal count of digits, - extend to wished count of digits or decimals */ - q = buf; - if (digits >= 0) { /* count digits */ - if (digits >= bufSize) return DECIMAL_BUFFER_OVERFLOW; - if (len == 0) { - /* zero or rounded to zero */ - *pDecPos = 1; - } else { - /* copy significant digits */ - for (i = 0; i < len; i++) { - *q++ = *(--p); - } - *pDecPos = sigDigits - scale; - } - /* add trailing zeros */ - for (i = len; i < digits; i++) { - *q++ = '0'; - } - } else { /* count decimals */ - if (scale >= sigDigits) { /* add leading zeros */ - if (decimals+2 >= bufSize) return DECIMAL_BUFFER_OVERFLOW; - *pDecPos = 1; - for (i = 0; i <= scale - sigDigits; i++) { - *q++ = '0'; - } - } else { - if (sigDigits - scale + decimals+1 >= bufSize) return DECIMAL_BUFFER_OVERFLOW; - *pDecPos = sigDigits - scale; - } - /* copy significant digits */ - for (i = 0; i < len; i++) { - *q++ = *(--p); - } - /* add trailing zeros */ - for (i = scale; i < decimals; i++) { - *q++ = '0'; - } - } - *q = 0; - - *pSign = (sigDigits > 0) ? pA->signscale.sign : 0; /* zero has positive sign */ - - return DECIMAL_SUCCESS; -} - /** * mono_decimal2UInt64: * @pA @@ -1296,8 +1261,8 @@ gint32 mono_decimalMult(/*[In, Out]*/decimal_repr* pA, /*[In]*/decimal_repr* pB) return pack128toDecimal(pA, low, mid, scale, sign); } -static int decimalDivSub(/*[In]*/decimal_repr* pA, /*[In]*/decimal_repr* pB, - guint64* pclo, guint64* pchi, int* pExp) +static DECINLINE int decimalDivSub(/*[In]*/decimal_repr* pA, /*[In]*/decimal_repr* pB, + guint64* pclo, guint64* pchi, int* pExp) { guint64 alo, ami, ahi; guint64 tlo, tmi, thi; @@ -1321,14 +1286,52 @@ static int decimalDivSub(/*[In]*/decimal_repr* pA, /*[In]*/decimal_repr* pB, } /* enlarge dividend to get maximal precision */ - for (ashift = 0; (ahi & LIT_GUINT64_HIGHBIT) == 0; ++ashift) { - lshift128(&ami, &ahi); - } + if (ahi == 0) { + ahi = ami; + ami = 0; + for (ashift = 64; (ahi & LIT_GUINT64_HIGHBIT) == 0; ++ashift) { + ahi <<= 1; + } + } else { + for (ashift = 0; (ahi & LIT_GUINT64_HIGHBIT) == 0; ++ashift) { + lshift128(&ami, &ahi); + } + } /* ensure that divisor is at least 2^95 */ - for (bshift = 0; (bhi & LIT_GUINT32_HIGHBIT) == 0; ++bshift) { - lshift96(&blo, &bmi, &bhi); - } + if (bhi == 0) { + + if (bmi == 0) { + guint32 hi_shift; + bhi = blo; + bmi = 0; + blo = 0; + + //g_assert (g_bit_nth_msf (bhi, 32) == my_g_bit_nth_msf (bhi)); + + hi_shift = 31 - my_g_bit_nth_msf (bhi); + bhi <<= hi_shift; + bshift = 64 + hi_shift; + } else { + bhi = bmi; + bmi = blo; + blo = 0; + + for (bshift = 32; (bhi & LIT_GUINT32_HIGHBIT) == 0; ++bshift) { + bhi <<= 1; + bhi |= (bmi & LIT_GUINT32_HIGHBIT) >> 31; + bmi <<= 1; + } + } + } else { + for (bshift = 0; (bhi & LIT_GUINT32_HIGHBIT) == 0; ++bshift) { + bhi <<= 1; + bhi |= (bmi & LIT_GUINT32_HIGHBIT) >> 31; + bmi <<= 1; + bmi |= (blo & LIT_GUINT32_HIGHBIT) >> 31; + blo <<= 1; + } + } thi = ((guint64)bhi)<<32 | bmi; tmi = ((guint64)blo)<<32; @@ -1368,6 +1371,16 @@ gint32 mono_decimalDiv(/*[Out]*/decimal_repr* pC, /*[In]*/decimal_repr* pA, /*[I MONO_ARCH_SAVE_REGS; + /* Check for common cases */ + if (mono_decimalCompare (pA, pB) == 0) + /* One */ + return pack128toDecimal (pC, 1, 0, 0, 0); + pA->signscale.sign = pA->signscale.sign ? 0 : 1; + if (mono_decimalCompare (pA, pB) == 0) + /* Minus one */ + return pack128toDecimal (pC, 1, 0, 0, 1); + pA->signscale.sign = pA->signscale.sign ? 0 : 1; + rc = decimalDivSub(pA, pB, &clo, &chi, &texp); if (rc != DECIMAL_SUCCESS) { if (rc == DECIMAL_FINISHED) rc = DECIMAL_SUCCESS; @@ -1468,17 +1481,6 @@ DECINLINE static void buildIEEE754Double(double* pd, int sign, int texp, guint64 PRECONDITION(sign == 0 || sign == 1); *p = (((guint64)sign) << 63) | (((guint64)((1023+texp)&0x7ff)) << 52) | mantisse; -#ifdef ARM_FPU_FPA -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - { - guint32 temp; - guint32 *t = (guint32*)p; - temp = t [0]; - t [0] = t [1]; - t [1] = temp; - } -#endif -#endif } double mono_decimal2double(/*[In]*/decimal_repr* pA) @@ -1571,4 +1573,3 @@ gint32 mono_decimalSetExponent(/*[In, Out]*/decimal_repr* pA, gint32 texp) } #endif /* DISABLE_DECIMAL */ -