2009-12-04 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / decimal.c
index daeac6e6c14034b2604e6b523dae46ff89c5e923..6524443eab3d57168325a710d2ac93e05d74671a 100644 (file)
@@ -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 
@@ -22,6 +22,9 @@
 #ifdef HAVE_MEMORY_H
 #include <memory.h>
 #endif
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
 
 #ifndef DISABLE_DECIMAL
 
@@ -565,41 +568,35 @@ my_g_bit_nth_msf (gsize mask)
        __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(__x86_64__) && defined(_MSC_VER)
+       unsigned long bIndex = 0;
+       if (_BitScanReverse64 (&bIndex, mask))
+               return bIndex;
+       return -1;
 #else
-       return g_bit_nth_msf (mask, sizeof (gsize) * 8);
+       int i;
+
+       i = sizeof (gsize) * 8;
+       while (i > 0) {
+               i --;
+               if (mask & (1UL << 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 */
@@ -683,7 +680,7 @@ DECINLINE static int adjustScale128(guint64* palo, guint64* pahi, int deltaScale
 DECINLINE static int rescale128(guint64* pclo, guint64* pchi, int* pScale, int texp,
                                 int minScale, int maxScale, int roundFlag)
 {
-    guint32 factor, overhang, prev_lo;
+    guint32 factor, overhang;
     int scale, i, rc, roundBit = 0;
 
     PRECONDITION(texp >= 0);
@@ -694,15 +691,38 @@ DECINLINE static int rescale128(guint64* pclo, guint64* pchi, int* pScale, int t
         /* reduce exp */
         while (texp > 0 && scale <= maxScale) {
             overhang = (guint32)(*pchi >> 32);
-                       prev_lo = *pclo;
+
+                       /* The original loop was this: */
+                       /*
+            while (texp > 0 && (overhang > (2<<DECIMAL_MAX_INTFACTORS) || (*pclo & 1) == 0)) {
+                               if (--texp == 0)
+                                       roundBit = (int)(*pclo & 1);
+                rshift128(pclo, pchi);
+                overhang = (guint32)(*pchi >> 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<<DECIMAL_MAX_INTFACTORS) || (*pclo & 1) == 0)) {
-                               --texp;
-                               prev_lo = *pclo;
+                               if (--texp == 0) roundBit = (int)(*pclo & 1);
                 rshift128(pclo, pchi);
                 overhang >>= 1;
             }
-                       if (texp == 0)
-                               roundBit = (int)(prev_lo & 1);
 
             if (texp > DECIMAL_MAX_INTFACTORS) i = DECIMAL_MAX_INTFACTORS;
             else i = texp;
@@ -1412,6 +1432,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;