Fix parsing decimal zero values. Fixes #6444
[mono.git] / mono / metadata / decimal.c
index d228976b35f19dde13ae6a394b01c4cacf90ed89..c8f22887d96161e27408163af3d003991892aef6 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,8 +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;
+#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
-       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
 }
 
@@ -659,7 +689,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);
@@ -680,12 +710,6 @@ DECINLINE static int rescale128(guint64* pclo, guint64* pchi, int* pScale, int t
                 overhang = (guint32)(*pchi >> 32);
             }
                        */
-
-                       prev_lo = *pclo;
-                       /*
-                        * FIXME: This code seems to cause crashes on the x86 buildbot during the
-                        * System.Data.DataSetExtensions tests.
-                        */
                        if (overhang > 0) {
                                int msf = my_g_bit_nth_msf (overhang);
                                int shift = msf - (DECIMAL_MAX_INTFACTORS + 2);
@@ -695,22 +719,19 @@ DECINLINE static int rescale128(guint64* pclo, guint64* pchi, int* pScale, int t
 
                                if (shift > 0) {
                                        texp -= shift;
-                                       prev_lo = (*pclo >> (shift - 1));
                                        *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;
@@ -755,6 +776,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)
 {
@@ -896,7 +945,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;
 
@@ -915,7 +968,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);
@@ -936,6 +989,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);
 }
 
@@ -1018,6 +1079,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 */
@@ -1043,8 +1108,7 @@ 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);