]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/fmtable.cpp
ICU-64232.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / fmtable.cpp
index 9fdef20c3ae984ca0be5d37c120f58181bea82b3..d813424494f8077e241f950cee5b0da7d83dcba7 100644 (file)
@@ -1,3 +1,5 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 *******************************************************************************
 * Copyright (C) 1997-2016, International Business Machines Corporation and
@@ -17,6 +19,7 @@
 
 #if !UCONFIG_NO_FORMATTING
 
+#include <cstdlib>
 #include <math.h>
 #include "unicode/fmtable.h"
 #include "unicode/ustring.h"
@@ -26,9 +29,8 @@
 #include "charstr.h"
 #include "cmemory.h"
 #include "cstring.h"
-#include "decNumber.h"
-#include "digitlst.h"
 #include "fmtableimp.h"
+#include "number_decimalquantity.h"
 
 // *****************************************************************************
 // class Formattable
@@ -38,6 +40,8 @@ U_NAMESPACE_BEGIN
 
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable)
 
+using number::impl::DecimalQuantity;
+
 
 //-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
 
@@ -101,7 +105,7 @@ void  Formattable::init() {
     fValue.fInt64 = 0;
     fType = kLong;
     fDecimalStr = NULL;
-    fDecimalNum = NULL;
+    fDecimalQuantity = NULL;
     fBogus.setToBogus(); 
 }
 
@@ -155,7 +159,7 @@ Formattable::Formattable(int64_t value)
 // -------------------------------------
 // Creates a formattable object with a decimal number value from a string.
 
-Formattable::Formattable(const StringPiece &number, UErrorCode &status) {
+Formattable::Formattable(StringPiece number, UErrorCode &status) {
     init();
     setDecimalNumber(number, status);
 }
@@ -255,8 +259,8 @@ Formattable::operator=(const Formattable& source)
         }
 
         UErrorCode status = U_ZERO_ERROR;
-        if (source.fDecimalNum != NULL) {
-          fDecimalNum = new DigitList(*source.fDecimalNum); // TODO: use internal digit list
+        if (source.fDecimalQuantity != NULL) {
+          fDecimalQuantity = new DecimalQuantity(*source.fDecimalQuantity);
         }
         if (source.fDecimalStr != NULL) {
             fDecimalStr = new CharString(*source.fDecimalStr, status);
@@ -354,14 +358,9 @@ void Formattable::dispose()
 
     delete fDecimalStr;
     fDecimalStr = NULL;
-    
-    FmtStackData *stackData = (FmtStackData*)fStackData;
-    if(fDecimalNum != &(stackData->stackDecimalNum)) {
-      delete fDecimalNum;
-    } else {
-      fDecimalNum->~DigitList(); // destruct, don't deallocate
-    }
-    fDecimalNum = NULL;
+
+    delete fDecimalQuantity;
+    fDecimalQuantity = NULL;
 }
 
 Formattable *
@@ -463,13 +462,13 @@ Formattable::getInt64(UErrorCode& status) const
         } else if (fValue.fDouble < (double)U_INT64_MIN) {
             status = U_INVALID_FORMAT_ERROR;
             return U_INT64_MIN;
-        } else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalNum != NULL) {
-            int64_t val = fDecimalNum->getInt64();
-            if (val != 0) {
-                return val;
+        } else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalQuantity != NULL) {
+            if (fDecimalQuantity->fitsInLong(true)) {
+                return fDecimalQuantity->toLong();
             } else {
+                // Unexpected
                 status = U_INVALID_FORMAT_ERROR;
-                return fValue.fDouble > 0 ? U_INT64_MAX : U_INT64_MIN;
+                return fDecimalQuantity->isNegative() ? U_INT64_MIN : U_INT64_MAX;
             }
         } else {
             return (int64_t)fValue.fDouble;
@@ -712,114 +711,108 @@ StringPiece Formattable::getDecimalNumber(UErrorCode &status) {
 
 CharString *Formattable::internalGetCharString(UErrorCode &status) {
     if(fDecimalStr == NULL) {
-      if (fDecimalNum == NULL) {
+      if (fDecimalQuantity == NULL) {
         // No decimal number for the formattable yet.  Which means the value was
         // set directly by the user as an int, int64 or double.  If the value came
         // from parsing, or from the user setting a decimal number, fDecimalNum
         // would already be set.
         //
-        fDecimalNum = new DigitList; // TODO: use internal digit list
-        if (fDecimalNum == NULL) {
-          status = U_MEMORY_ALLOCATION_ERROR;
-          return NULL;
-        }
-
-        switch (fType) {
-        case kDouble:
-          fDecimalNum->set(this->getDouble());
-          break;
-        case kLong:
-          fDecimalNum->set(this->getLong());
-          break;
-        case kInt64:
-          fDecimalNum->set(this->getInt64());
-          break;
-        default:
-          // The formattable's value is not a numeric type.
-          status = U_INVALID_STATE_ERROR;
-          return NULL;
-        }
+        LocalPointer<DecimalQuantity> dq(new DecimalQuantity(), status);
+        if (U_FAILURE(status)) { return nullptr; }
+        populateDecimalQuantity(*dq, status);
+        if (U_FAILURE(status)) { return nullptr; }
+        fDecimalQuantity = dq.orphan();
       }
 
-      fDecimalStr = new CharString;
+      fDecimalStr = new CharString();
       if (fDecimalStr == NULL) {
         status = U_MEMORY_ALLOCATION_ERROR;
         return NULL;
       }
-      fDecimalNum->getDecimal(*fDecimalStr, status);
+      // Older ICUs called uprv_decNumberToString here, which is not exactly the same as
+      // DecimalQuantity::toScientificString(). The biggest difference is that uprv_decNumberToString does
+      // not print scientific notation for magnitudes greater than -5 and smaller than some amount (+5?).
+      if (fDecimalQuantity->isInfinite()) {
+        fDecimalStr->append("Infinity", status);
+      } else if (fDecimalQuantity->isNaN()) {
+        fDecimalStr->append("NaN", status);
+      } else if (fDecimalQuantity->isZero()) {
+        fDecimalStr->append("0", -1, status);
+      } else if (fType==kLong || fType==kInt64 || // use toPlainString for integer types
+                  (fDecimalQuantity->getMagnitude() != INT32_MIN && std::abs(fDecimalQuantity->getMagnitude()) < 5)) {
+        fDecimalStr->appendInvariantChars(fDecimalQuantity->toPlainString(), status);
+      } else {
+        fDecimalStr->appendInvariantChars(fDecimalQuantity->toScientificString(), status);
+      }
     }
     return fDecimalStr;
 }
 
+void
+Formattable::populateDecimalQuantity(number::impl::DecimalQuantity& output, UErrorCode& status) const {
+    if (fDecimalQuantity != nullptr) {
+        output = *fDecimalQuantity;
+        return;
+    }
 
-DigitList *
-Formattable::getInternalDigitList() {
-  FmtStackData *stackData = (FmtStackData*)fStackData;
-  if(fDecimalNum != &(stackData->stackDecimalNum)) {
-    delete fDecimalNum;
-    fDecimalNum = new (&(stackData->stackDecimalNum), kOnStack) DigitList();
-  } else {
-    fDecimalNum->clear();
-  }
-  return fDecimalNum;
+    switch (fType) {
+        case kDouble:
+            output.setToDouble(this->getDouble());
+            output.roundToInfinity();
+            break;
+        case kLong:
+            output.setToInt(this->getLong());
+            break;
+        case kInt64:
+            output.setToLong(this->getInt64());
+            break;
+        default:
+            // The formattable's value is not a numeric type.
+            status = U_INVALID_STATE_ERROR;
+    }
 }
 
 // ---------------------------------------
 void
-Formattable::adoptDigitList(DigitList *dl) {
-  if(fDecimalNum==dl) {
-    fDecimalNum = NULL; // don't delete
-  }
-  dispose();
-
-  fDecimalNum = dl;
-
-  if(dl==NULL) { // allow adoptDigitList(NULL) to clear
-    return;
-  }
+Formattable::adoptDecimalQuantity(DecimalQuantity *dq) {
+    if (fDecimalQuantity != NULL) {
+        delete fDecimalQuantity;
+    }
+    fDecimalQuantity = dq;
+    if (dq == NULL) { // allow adoptDigitList(NULL) to clear
+        return;
+    }
 
     // Set the value into the Union of simple type values.
-    // Cannot use the set() functions because they would delete the fDecimalNum value,
-
-    if (fDecimalNum->fitsIntoLong(FALSE)) {
-        fType = kLong;
-        fValue.fInt64 = fDecimalNum->getLong();
-    } else if (fDecimalNum->fitsIntoInt64(FALSE)) {
-        fType = kInt64;
-        fValue.fInt64 = fDecimalNum->getInt64();
+    // Cannot use the set() functions because they would delete the fDecimalNum value.
+    if (fDecimalQuantity->fitsInLong()) {
+        fValue.fInt64 = fDecimalQuantity->toLong();
+        if (fValue.fInt64 <= INT32_MAX && fValue.fInt64 >= INT32_MIN) {
+            fType = kLong;
+        } else {
+            fType = kInt64;
+        }
     } else {
         fType = kDouble;
-        fValue.fDouble = fDecimalNum->getDouble();
+        fValue.fDouble = fDecimalQuantity->toDouble();
     }
 }
 
 
 // ---------------------------------------
 void
-Formattable::setDecimalNumber(const StringPiece &numberString, UErrorCode &status) {
+Formattable::setDecimalNumber(StringPiece numberString, UErrorCode &status) {
     if (U_FAILURE(status)) {
         return;
     }
     dispose();
 
-    // Copy the input string and nul-terminate it.
-    //    The decNumber library requires nul-terminated input.  StringPiece input
-    //    is not guaranteed nul-terminated.  Too bad.
-    //    CharString automatically adds the nul.
-    DigitList *dnum = new DigitList(); // TODO: use getInternalDigitList
-    if (dnum == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return;
-    }
-    dnum->set(CharString(numberString, status).toStringPiece(), status);
-    if (U_FAILURE(status)) {
-        delete dnum;
-        return;   // String didn't contain a decimal number.
-    }
-    adoptDigitList(dnum);
+    auto* dq = new DecimalQuantity();
+    dq->setToDecNumber(numberString, status);
+    adoptDecimalQuantity(dq);
 
     // Note that we do not hang on to the caller's input string.
-    // If we are asked for the string, we will regenerate one from fDecimalNum.
+    // If we are asked for the string, we will regenerate one from fDecimalQuantity.
 }
 
 #if 0