]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/digitlst.cpp
ICU-551.24.tar.gz
[apple/icu.git] / icuSources / i18n / digitlst.cpp
index a99f4d8ed13fbf2f4347d844cd26e70e72bf0451..51738bec027d5e98dfcb5cc9ecb4e70182858184 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-*   Copyright (C) 1997-2011, International Business Machines
+*   Copyright (C) 1997-2015, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 **********************************************************************
 *
@@ -31,6 +31,7 @@
 #include "charstr.h"
 #include "cmemory.h"
 #include "cstring.h"
+#include "mutex.h"
 #include "putilimp.h"
 #include "uassert.h"
 #include <stdlib.h>
 #include <stdio.h>
 #include <limits>
 
-#if defined(U_DARWIN)
-#include <xlocale.h>
-#include <xlocale/_stdio.h>
-#endif
-
 // ***************************************************************************
 // class DigitList
 //    A wrapper onto decNumber.
  */
 #define kZero '0'
 
-static char gDecimal = 0;
 
 /* Only for 32 bit numbers. Ignore the negative sign. */
-static const char LONG_MIN_REP[] = "2147483648";
-static const char I64_MIN_REP[] = "9223372036854775808";
+//static const char LONG_MIN_REP[] = "2147483648";
+//static const char I64_MIN_REP[] = "9223372036854775808";
 
 
 U_NAMESPACE_BEGIN
@@ -78,8 +73,7 @@ DigitList::DigitList()
     fDecNumber = fStorage.getAlias();
     uprv_decNumberZero(fDecNumber);
 
-    fDouble = 0.0;
-    fHaveDouble = TRUE;
+    internalSetDouble(0.0);
 }
 
 // -------------------------------------
@@ -116,8 +110,19 @@ DigitList::operator=(const DigitList& other)
         fContext.digits = fStorage.getCapacity();
         uprv_decNumberCopy(fDecNumber, other.fDecNumber);
 
-        fDouble = other.fDouble;
-        fHaveDouble = other.fHaveDouble;
+        {
+            // fDouble is lazily created and cached.
+            // Avoid potential races with that happening with other.fDouble
+            // while we are doing the assignment.
+            Mutex mutex;
+
+            if(other.fHave==kDouble) {
+              fUnion.fDouble = other.fUnion.fDouble;
+            } else if(other.fHave==kInt64) {
+              fUnion.fInt64 = other.fUnion.fInt64;
+            }
+            fHave = other.fHave;
+        }
     }
     return *this;
 }
@@ -189,8 +194,7 @@ DigitList::clear()
 {
     uprv_decNumberZero(fDecNumber);
     uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN);
-    fDouble = 0.0;
-    fHaveDouble = TRUE;
+    internalSetDouble(0.0);
 }
 
 
@@ -235,6 +239,15 @@ formatBase10(int64_t number, char *outputStr) {
 
 
 // -------------------------------------
+//
+//  setRoundingMode()
+//    For most modes, the meaning and names are the same between the decNumber library
+//      (which DigitList follows) and the ICU Formatting Rounding Mode values.
+//      The flag constants are different, however.
+//
+//     Note that ICU's kRoundingUnnecessary is not implemented directly by DigitList.
+//     This mode, inherited from Java, means that numbers that would not format exactly
+//     will return an error when formatting is attempted.
 
 void 
 DigitList::setRoundingMode(DecimalFormat::ERoundingMode m) {
@@ -248,6 +261,7 @@ DigitList::setRoundingMode(DecimalFormat::ERoundingMode m) {
       case  DecimalFormat::kRoundHalfEven: r = DEC_ROUND_HALF_EVEN; break;
       case  DecimalFormat::kRoundHalfDown: r = DEC_ROUND_HALF_DOWN; break;
       case  DecimalFormat::kRoundHalfUp:   r = DEC_ROUND_HALF_UP;   break;
+      case  DecimalFormat::kRoundUnnecessary: r = DEC_ROUND_HALF_EVEN; break;
       default:
          // TODO: how to report the problem?
          // Leave existing mode unchanged.
@@ -267,7 +281,7 @@ DigitList::setPositive(UBool s) {
     } else {
         fDecNumber->bits |= DECNEG;
     }
-    fHaveDouble = FALSE;
+    internalClear();
 }
 // -------------------------------------
 
@@ -282,7 +296,7 @@ DigitList::setDecimalAt(int32_t d) {
         adjustedDigits = 0;
     }
     fDecNumber->exponent = d - adjustedDigits;
-    fHaveDouble = FALSE;
+    internalClear();
 }
 
 int32_t  
@@ -304,7 +318,7 @@ DigitList::setCount(int32_t c)  {
         fDecNumber->lsu[0] = 0;
     }
     fDecNumber->digits = c;
-    fHaveDouble = FALSE;
+    internalClear();
 }
 
 int32_t  
@@ -325,7 +339,7 @@ DigitList::setDigit(int32_t i, char v) {
     U_ASSERT(v>='0' && v<='9');
     v &= 0x0f;
     fDecNumber->lsu[count-i-1] = v;
-    fHaveDouble = FALSE;
+    internalClear();
 }
 
 char     
@@ -379,13 +393,13 @@ DigitList::append(char digit)
             fDecNumber->exponent--;
         }
     }
-    fHaveDouble = FALSE;
+    internalClear();
 }
 
 // -------------------------------------
 
 /**
- * Currently, getDouble() depends on atof() to do its conversion.
+ * Currently, getDouble() depends on strtod() to do its conversion.
  *
  * WARNING!!
  * This is an extremely costly function. ~1/2 of the conversion time
@@ -394,37 +408,42 @@ DigitList::append(char digit)
 double
 DigitList::getDouble() const
 {
-    // TODO:  fix thread safety.  Can probably be finessed some by analyzing
-    //        what public const functions can see which DigitLists.
-    //        Like precompute fDouble for DigitLists coming in from a parse
-    //        or from a Formattable::set(), but not for any others.
-    if (fHaveDouble) {
-        return fDouble;
+    static char gDecimal = 0;
+    char decimalSeparator;
+    {
+        Mutex mutex;
+        if (fHave == kDouble) {
+            return fUnion.fDouble;
+        } else if(fHave == kInt64) {
+            return (double)fUnion.fInt64;
+        }
+        decimalSeparator = gDecimal;
     }
-    DigitList *nonConstThis = const_cast<DigitList *>(this);
 
-    if (gDecimal == 0) {
+    if (decimalSeparator == 0) {
+        // We need to know the decimal separator character that will be used with strtod().
+        // Depends on the C runtime global locale.
+        // Most commonly is '.'
+        // TODO: caching could fail if the global locale is changed on the fly.
         char rep[MAX_DIGITS];
-        // For machines that decide to change the decimal on you,
-        // and try to be too smart with localization.
-        // This normally should be just a '.'.
         sprintf(rep, "%+1.1f", 1.0);
-        gDecimal = rep[2];
+        decimalSeparator = rep[2];
     }
 
+    double tDouble = 0.0;
     if (isZero()) {
-        nonConstThis->fDouble = 0.0;
+        tDouble = 0.0;
         if (decNumberIsNegative(fDecNumber)) {
-            nonConstThis->fDouble /= -1;
+            tDouble /= -1;
         }
     } else if (isInfinite()) {
         if (std::numeric_limits<double>::has_infinity) {
-            nonConstThis->fDouble = std::numeric_limits<double>::infinity();
+            tDouble = std::numeric_limits<double>::infinity();
         } else {
-            nonConstThis->fDouble = std::numeric_limits<double>::max();
+            tDouble = std::numeric_limits<double>::max();
         }
         if (!isPositive()) {
-            nonConstThis->fDouble = -fDouble;
+            tDouble = -tDouble; //this was incorrectly "-fDouble" originally.
         } 
     } else {
         MaybeStackArray<char, MAX_DBL_DIGITS+18> s;
@@ -439,24 +458,29 @@ DigitList::getDouble() const
             DigitList numToConvert(*this);
             numToConvert.reduce();    // Removes any trailing zeros, so that digit count is good.
             numToConvert.round(MAX_DBL_DIGITS+3);
-            uprv_decNumberToString(numToConvert.fDecNumber, s);
+            uprv_decNumberToString(numToConvert.fDecNumber, s.getAlias());
             // TODO:  how many extra digits should be included for an accurate conversion?
         } else {
-            uprv_decNumberToString(this->fDecNumber, s);
+            uprv_decNumberToString(this->fDecNumber, s.getAlias());
         }
         U_ASSERT(uprv_strlen(&s[0]) < MAX_DBL_DIGITS+18);
         
-        if (gDecimal != '.') {
-            char *decimalPt = strchr(s, '.');
+        if (decimalSeparator != '.') {
+            char *decimalPt = strchr(s.getAlias(), '.');
             if (decimalPt != NULL) {
-                *decimalPt = gDecimal;
+                *decimalPt = decimalSeparator;
             }
         }
         char *end = NULL;
-        nonConstThis->fDouble = uprv_strtod(s, &end);
+        tDouble = uprv_strtod(s.getAlias(), &end);
+    }
+    {
+        Mutex mutex;
+        DigitList *nonConstThis = const_cast<DigitList *>(this);
+        nonConstThis->internalSetDouble(tDouble);
+        gDecimal = decimalSeparator;
     }
-    nonConstThis->fHaveDouble = TRUE;
-    return fDouble;
+    return tDouble;
 }
 
 // -------------------------------------
@@ -491,6 +515,9 @@ int32_t DigitList::getLong() /*const*/
  *  Return zero if the number cannot be represented.
  */
 int64_t DigitList::getInt64() /*const*/ {
+    if(fHave==kInt64) {
+      return fUnion.fInt64;
+    } 
     // Truncate if non-integer.
     // Return 0 if out of range.
     // Range of in64_t is -9223372036854775808 to 9223372036854775807  (19 digits)
@@ -662,14 +689,12 @@ void
 DigitList::set(int32_t source)
 {
     set((int64_t)source);
-    fDouble = source;
-    fHaveDouble = TRUE;
+    internalSetDouble(source);
 }
 
 // -------------------------------------
 /**
- * @param maximumDigits The maximum digits to be generated.  If zero,
- * there is no maximum -- generate all digits.
+ * Set an int64, via decnumber
  */
 void
 DigitList::set(int64_t source)
@@ -679,8 +704,17 @@ DigitList::set(int64_t source)
     U_ASSERT(uprv_strlen(str) < sizeof(str));
 
     uprv_decNumberFromString(fDecNumber, str, &fContext);
-    fDouble = (double)source;
-    fHaveDouble = TRUE;
+    internalSetDouble(static_cast<double>(source));
+}
+
+/**
+ * Set an int64, with no decnumber
+ */
+void
+DigitList::setInteger(int64_t source)
+{
+  fDecNumber=NULL;
+  internalSetInt64(source);
 }
 
 
@@ -694,31 +728,58 @@ DigitList::set(int64_t source)
  * be acceptable for a public API.
  */
 void
-DigitList::set(const StringPiece &source, UErrorCode &status) {
+DigitList::set(const StringPiece &source, UErrorCode &status, uint32_t /*fastpathBits*/) {
     if (U_FAILURE(status)) {
         return;
     }
 
-    // Figure out a max number of digits to use during the conversion, and
-    // resize the number up if necessary.
-    int32_t numDigits = source.length();
-    if (numDigits > fContext.digits) {
+#if 0    
+    if(fastpathBits==(kFastpathOk|kNoDecimal)) {
+      int32_t size = source.size();
+      const char *data = source.data();
+      int64_t r = 0;
+      int64_t m = 1;
+      // fast parse
+      while(size>0) {
+        char ch = data[--size];
+        if(ch=='+') {
+          break;
+        } else if(ch=='-') {
+          r = -r;
+          break;
+        } else {
+          int64_t d = ch-'0';
+          //printf("CH[%d]=%c, %d, *=%d\n", size,ch, (int)d, (int)m);
+          r+=(d)*m;
+          m *= 10;
+        }
+      }
+      //printf("R=%d\n", r);
+      set(r);
+    } else
+#endif
+        {
+      // Figure out a max number of digits to use during the conversion, and
+      // resize the number up if necessary.
+      int32_t numDigits = source.length();
+      if (numDigits > fContext.digits) {
         // fContext.digits == fStorage.getCapacity()
         decNumber *t = fStorage.resize(numDigits, fStorage.getCapacity());
         if (t == NULL) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-            return;
+          status = U_MEMORY_ALLOCATION_ERROR;
+          return;
         }
         fDecNumber = t;
         fContext.digits = numDigits;
-    }
+      }
 
-    fContext.status = 0;
-    uprv_decNumberFromString(fDecNumber, source.data(), &fContext);
-    if ((fContext.status & DEC_Conversion_syntax) != 0) {
+      fContext.status = 0;
+      uprv_decNumberFromString(fDecNumber, source.data(), &fContext);
+      if ((fContext.status & DEC_Conversion_syntax) != 0) {
         status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
+      }
     }
-    fHaveDouble = FALSE;
+    internalClear();
 }   
 
 /**
@@ -732,22 +793,33 @@ DigitList::set(double source)
     // for now, simple implementation; later, do proper IEEE stuff
     char rep[MAX_DIGITS + 8]; // Extra space for '+', '.', e+NNN, and '\0' (actually +8 is enough)
 
-    // Generate a representation of the form /[+-][0-9]+e[+-][0-9]+/
-#if defined(U_DARWIN)
-    // Use NULL "C" locale_t with xlocale sprintf_l API to produce "." decimal separator
-    // as expected by uprv_decNumberFromString, independently of whatever locale may be set
-    // via libC API, i.e. setlocale(LC_ALL, <explicit-locale>).
-    sprintf_l(rep, NULL, "%+1.*e", MAX_DBL_DIGITS - 1, source);
-#else
-    sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source);
-#endif
+    // Generate a representation of the form /[+-][0-9].[0-9]+e[+-][0-9]+/
+    // Can also generate /[+-]nan/ or /[+-]inf/
+    // TODO: Use something other than sprintf() here, since it's behavior is somewhat platform specific.
+    //       That is why infinity is special cased here.
+    if (uprv_isInfinite(source)) {
+        if (uprv_isNegativeInfinity(source)) {
+            uprv_strcpy(rep,"-inf"); // Handle negative infinity
+        } else {
+            uprv_strcpy(rep,"inf");
+        }
+    } else {
+        sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source);
+    }
     U_ASSERT(uprv_strlen(rep) < sizeof(rep));
 
+    // uprv_decNumberFromString() will parse the string expecting '.' as a
+    // decimal separator, however sprintf() can use ',' in certain locales.
+    // Overwrite a ',' with '.' here before proceeding.
+    char *decimalSeparator = strchr(rep, ',');
+    if (decimalSeparator != NULL) {
+        *decimalSeparator = '.';
+    }
+
     // Create a decNumber from the string.
     uprv_decNumberFromString(fDecNumber, rep, &fContext);
     uprv_decNumberTrim(fDecNumber);
-    fDouble = source;
-    fHaveDouble = TRUE;
+    internalSetDouble(source);
 }
 
 // -------------------------------------
@@ -768,7 +840,7 @@ DigitList::mult(const DigitList &other, UErrorCode &status) {
         ensureCapacity(requiredDigits, status);
     }
     uprv_decNumberMultiply(fDecNumber, fDecNumber, other.fDecNumber, &fContext);
-    fHaveDouble = FALSE;
+    internalClear();
 }
 
 // -------------------------------------
@@ -785,7 +857,7 @@ DigitList::div(const DigitList &other, UErrorCode &status) {
         return;
     }
     uprv_decNumberDivide(fDecNumber, fDecNumber, other.fDecNumber, &fContext);
-    fHaveDouble = FALSE;
+    internalClear();
 }
 
 // -------------------------------------
@@ -835,7 +907,7 @@ DigitList::round(int32_t maximumDigits)
     uprv_decNumberPlus(fDecNumber, fDecNumber, &fContext);
     fContext.digits = savedDigits;
     uprv_decNumberTrim(fDecNumber);
-    fHaveDouble = FALSE;
+    internalClear();
 }
 
 
@@ -852,7 +924,7 @@ DigitList::roundFixedPoint(int32_t maximumFractionDigits) {
     
     uprv_decNumberQuantize(fDecNumber, fDecNumber, &scale, &fContext);
     trim();
-    fHaveDouble = FALSE;
+    internalClear();
 }
 
 // -------------------------------------
@@ -870,7 +942,6 @@ DigitList::isZero() const
     return decNumberIsZero(fDecNumber);
 }
 
-
 U_NAMESPACE_END
 #endif // #if !UCONFIG_NO_FORMATTING