]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/decimfmt.cpp
ICU-551.51.4.tar.gz
[apple/icu.git] / icuSources / i18n / decimfmt.cpp
index e9280bc6c27098f1024c1fe1e84f021f5e097a45..5473c897bd81a79520ec9824ac034c76773f0374 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* Copyright (C) 1997-2014, International Business Machines Corporation and    *
+* Copyright (C) 1997-2015, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 *
@@ -72,6 +72,7 @@
 #include "dcfmtimp.h"
 #include "plurrule_impl.h"
 #include "decimalformatpattern.h"
+#include "fmtableimp.h"
 
 /*
  * On certain platforms, round is a macro defined in math.h
@@ -420,6 +421,7 @@ DecimalFormat::init() {
     fAffixesForCurrency = NULL;
     fPluralAffixesForCurrency = NULL;
     fCurrencyPluralInfo = NULL;
+    fCurrencyUsage = UCURR_USAGE_STANDARD;
 #if UCONFIG_HAVE_PARSEALLINPUT
     fParseAllInput = UNUM_MAYBE;
 #endif
@@ -635,7 +637,7 @@ DecimalFormat::setupCurrencyAffixPatterns(UErrorCode& status) {
     // save the unique currency plural patterns of this locale.
     Hashtable* pluralPtn = fCurrencyPluralInfo->fPluralCountToCurrencyUnitPattern;
     const UHashElement* element = NULL;
-    int32_t pos = -1;
+    int32_t pos = UHASH_FIRST;
     Hashtable pluralPatternSet;
     while ((element = pluralPtn->nextElement(pos)) != NULL) {
         const UHashTok valueTok = element->value;
@@ -813,6 +815,7 @@ DecimalFormat::operator=(const DecimalFormat& rhs)
         fMaxSignificantDigits = rhs.fMaxSignificantDigits;
         fUseSignificantDigits = rhs.fUseSignificantDigits;
         fFormatPattern = rhs.fFormatPattern;
+        fCurrencyUsage = rhs.fCurrencyUsage;
         fStyle = rhs.fStyle;
         _clone_ptr(&fCurrencyPluralInfo, rhs.fCurrencyPluralInfo);
         deleteHashForAffixPattern();
@@ -1087,7 +1090,9 @@ DecimalFormat::operator==(const Format& that) const
         ((fCurrencyPluralInfo == other->fCurrencyPluralInfo &&
           fCurrencyPluralInfo == NULL) ||
          (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo != NULL &&
-         *fCurrencyPluralInfo == *(other->fCurrencyPluralInfo)))
+         *fCurrencyPluralInfo == *(other->fCurrencyPluralInfo))) &&
+
+        fCurrencyUsage == other->fCurrencyUsage
 
         // depending on other settings we may also need to compare
         // fCurrencyChoice (mostly deprecated?),
@@ -1134,11 +1139,6 @@ DecimalFormat::getFixedDecimal(double number, UErrorCode &status) const {
     return result;
 }
 
-// MSVC optimizer bug? 
-// turn off optimization as it causes different behavior in the int64->double->int64 conversion
-#if defined (_MSC_VER)
-#pragma optimize ( "", off )
-#endif
 FixedDecimal
 DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) const {
     if (U_FAILURE(status)) {
@@ -1160,17 +1160,9 @@ DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) co
         return getFixedDecimal(number.getDouble(status), status);
     }
 
-    if (type == Formattable::kInt64) {
-        // "volatile" here is a workaround to avoid optimization issues.
-        volatile double fdv = number.getDouble(status);
-        // Note: conversion of int64_t -> double rounds with some compilers to
-        //       values beyond what can be represented as a 64 bit int. Subsequent
-        //       testing or conversion with int64_t produces bad results.
-        //       So filter the problematic values, route them to DigitList.
-        if (fdv != (double)U_INT64_MAX && fdv != (double)U_INT64_MIN &&
-                number.getInt64() == (int64_t)fdv) {
-            return getFixedDecimal(number.getDouble(status), status);
-        }
+    if (type == Formattable::kInt64 && number.getInt64() <= MAX_INT64_IN_DOUBLE &&
+                                       number.getInt64() >= -MAX_INT64_IN_DOUBLE) {
+        return getFixedDecimal(number.getDouble(status), status);
     }
 
     // The only case left is type==int64_t, with a value with more digits than a double can represent.
@@ -1182,10 +1174,6 @@ DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) co
     digits.set(number.getInt64());
     return getFixedDecimal(digits, status);
 }
-// end workaround MSVC optimizer bug
-#if defined (_MSC_VER)
-#pragma optimize ( "", on )
-#endif
 
 
 // Create a fixed decimal from a DigitList.
@@ -1320,14 +1308,7 @@ void DecimalFormat::handleChanged() {
     debug("parse fastpath: YES");
   }
   
-  if (fGroupingSize!=0 && isGroupingUsed()) {
-    debug("No format fastpath: fGroupingSize!=0 and grouping is used");
-#ifdef FMT_DEBUG
-    printf("groupingsize=%d\n", fGroupingSize);
-#endif
-  } else if(fGroupingSize2!=0 && isGroupingUsed()) {
-    debug("No format fastpath: fGroupingSize2!=0");
-  } else if(fUseExponentialNotation) {
+  if(fUseExponentialNotation) {
     debug("No format fastpath: fUseExponentialNotation");
   } else if(fFormatWidth!=0) {
     debug("No format fastpath: fFormatWidth!=0");
@@ -1347,6 +1328,17 @@ void DecimalFormat::handleChanged() {
     debug("No format fastpath: fCurrencySignCount != fgCurrencySignCountZero");
   } else if(fRoundingIncrement!=0) {
     debug("No format fastpath: fRoundingIncrement!=0");
+  } else if (fGroupingSize!=0 && isGroupingUsed()) {
+    debug("Maybe format fastpath: fGroupingSize!=0 and grouping is used");
+#ifdef FMT_DEBUG
+    printf("groupingsize=%d\n", fGroupingSize);
+#endif
+    
+    if (getMinimumIntegerDigits() <= fGroupingSize) {
+      data.fFastFormatStatus = kFastpathMAYBE;
+    }
+  } else if(fGroupingSize2!=0 && isGroupingUsed()) {
+    debug("No format fastpath: fGroupingSize2!=0");
   } else {
     data.fFastFormatStatus = kFastpathYES;
     debug("format:kFastpathYES!");
@@ -1410,7 +1402,9 @@ DecimalFormat::_format(int64_t number,
   printf("fastpath? [%d]\n", number);
 #endif
     
-  if( data.fFastFormatStatus==kFastpathYES) {
+  if( data.fFastFormatStatus==kFastpathYES || 
+      data.fFastFormatStatus==kFastpathMAYBE) {
+    int32_t noGroupingThreshold = 0;
 
 #define kZero 0x0030
     const int32_t MAX_IDX = MAX_DIGITS+2;
@@ -1418,6 +1412,9 @@ DecimalFormat::_format(int64_t number,
     int32_t destIdx = MAX_IDX;
     outputStr[--destIdx] = 0;  // term
 
+    if (data.fFastFormatStatus==kFastpathMAYBE) {
+      noGroupingThreshold = destIdx - fGroupingSize;
+    }
     int64_t  n = number;
     if (number < 1) {
       // Negative numbers are slightly larger than positive
@@ -1427,15 +1424,19 @@ DecimalFormat::_format(int64_t number,
     }
     // get any remaining digits
     while (n > 0) {
+      if (destIdx == noGroupingThreshold) {
+        goto slowPath;
+      }
       outputStr[--destIdx] = (n % 10) + kZero;
       n /= 10;
     }
-    
 
         // Slide the number to the start of the output str
     U_ASSERT(destIdx >= 0);
     int32_t length = MAX_IDX - destIdx -1;
-    /*int32_t prefixLen = */ appendAffix(appendTo, number, handler, number<0, TRUE);
+    /*int32_t prefixLen = */ appendAffix(appendTo, static_cast<double>(number), handler, number<0, TRUE);
+
+    // This will be at least 0 even if it was set to a negative number.
     int32_t maxIntDig = getMaximumIntegerDigits();
     int32_t destlength = length<=maxIntDig?length:maxIntDig; // dest length pinned to max int digits
 
@@ -1443,7 +1444,10 @@ DecimalFormat::_format(int64_t number,
       status = U_ILLEGAL_ARGUMENT_ERROR;
     }
 
-    int32_t prependZero = getMinimumIntegerDigits() - destlength;
+    int32_t minDigits = getMinimumIntegerDigits();
+
+    // We always want at least one digit, even if it is just a 0.
+    int32_t prependZero = (minDigits < 1 ? 1 : minDigits) - destlength;
 
 #ifdef FMT_DEBUG
     printf("prependZero=%d, length=%d, minintdig=%d maxintdig=%d destlength=%d skip=%d\n", prependZero, length, getMinimumIntegerDigits(), maxIntDig, destlength, length-destlength);
@@ -1459,7 +1463,7 @@ DecimalFormat::_format(int64_t number,
                     destlength);
     handler.addAttribute(kIntegerField, intBegin, appendTo.length());
 
-    /*int32_t suffixLen =*/ appendAffix(appendTo, number, handler, number<0, FALSE);
+    /*int32_t suffixLen =*/ appendAffix(appendTo, static_cast<double>(number), handler, number<0, FALSE);
 
     //outputStr[length]=0;
     
@@ -1472,6 +1476,7 @@ DecimalFormat::_format(int64_t number,
     return appendTo;
   } // end fastpath
 #endif
+  slowPath:
 
   // Else the slow way - via DigitList
     DigitList digits;
@@ -2193,7 +2198,7 @@ CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text,
     parse(text, parseResult, pos, curbuf);
     if (pos.getIndex() != start) {
         UErrorCode ec = U_ZERO_ERROR;
-        LocalPointer<CurrencyAmount> currAmt(new CurrencyAmount(parseResult, curbuf, ec));
+        LocalPointer<CurrencyAmount> currAmt(new CurrencyAmount(parseResult, curbuf, ec), ec);
         if (U_FAILURE(ec)) {
             pos.setIndex(start); // indicate failure
         } else {
@@ -2375,7 +2380,7 @@ DecimalFormat::parseForCurrency(const UnicodeString& text,
     }
     // Then, parse against affix patterns.
     // Those are currency patterns and currency plural patterns.
-    int32_t pos = -1;
+    int32_t pos = UHASH_FIRST;
     const UHashElement* element = NULL;
     while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) {
         const UHashTok valueTok = element->value;
@@ -2937,6 +2942,18 @@ UBool DecimalFormat::subparse(const UnicodeString& text,
             }
         }
 
+        // if we didn't see a decimal and it is required, check to see if the pattern had one
+        if(!sawDecimal && isDecimalPatternMatchRequired()) 
+        {
+            if(fFormatPattern.indexOf(DecimalFormatSymbols::kDecimalSeparatorSymbol) != 0) 
+            {
+                parsePosition.setIndex(oldStart);
+                parsePosition.setErrorIndex(position);
+                debug("decimal point match required fail!");
+                return FALSE;
+            }
+        }
+
         if (backup != -1)
         {
             position = backup;
@@ -3050,6 +3067,20 @@ printf("PP -> %d, SLOW = [%s]!    pp=%d, os=%d, err=%s\n", position, parsedNum.d
         parsePosition.setErrorIndex(position);
         return FALSE;
     }
+
+    // check if we missed a required decimal point
+    if(fastParseOk && isDecimalPatternMatchRequired()) 
+    {
+        if(fFormatPattern.indexOf(DecimalFormatSymbols::kDecimalSeparatorSymbol) != 0) 
+        {
+            parsePosition.setIndex(oldStart);
+            parsePosition.setErrorIndex(position);
+            debug("decimal point match required fail!");
+            return FALSE;
+        }
+    }
+
+
     return TRUE;
 }
 
@@ -4106,7 +4137,7 @@ void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) {
 int32_t
 DecimalFormat::getGroupingSize() const
 {
-    return fGroupingSize;
+    return isGroupingUsed() ? fGroupingSize : 0;
 }
 
 //------------------------------------------------------------------------------
@@ -4161,6 +4192,24 @@ DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue)
 #endif
 }
 
+//------------------------------------------------------------------------------
+// Checks if decimal point pattern match is required
+UBool 
+DecimalFormat::isDecimalPatternMatchRequired(void) const
+{
+    return fBoolFlags.contains(UNUM_PARSE_DECIMAL_MARK_REQUIRED);
+}
+
+//------------------------------------------------------------------------------
+// Checks if decimal point pattern match is required
+         
+void 
+DecimalFormat::setDecimalPatternMatchRequired(UBool newValue)
+{
+    fBoolFlags.set(UNUM_PARSE_DECIMAL_MARK_REQUIRED, newValue);
+}
+
+
 //------------------------------------------------------------------------------
 // Emits the pattern of this DecimalFormat instance.
 
@@ -5152,8 +5201,8 @@ void DecimalFormat::setCurrencyInternally(const UChar* theCurrency,
     double rounding = 0.0;
     int32_t frac = 0;
     if (fCurrencySignCount != fgCurrencySignCountZero && isCurr) {
-        rounding = ucurr_getRoundingIncrement(theCurrency, &ec);
-        frac = ucurr_getDefaultFractionDigits(theCurrency, &ec);
+        rounding = ucurr_getRoundingIncrementForUsage(theCurrency, fCurrencyUsage, &ec);
+        frac = ucurr_getDefaultFractionDigitsForUsage(theCurrency, fCurrencyUsage, &ec);
     }
 
     NumberFormat::setCurrency(theCurrency, ec);
@@ -5189,6 +5238,28 @@ void DecimalFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) {
 #endif
 }
 
+void DecimalFormat::setCurrencyUsage(UCurrencyUsage newContext, UErrorCode* ec){
+    fCurrencyUsage = newContext;
+
+    const UChar* theCurrency = getCurrency();
+
+    // We set rounding/digit based on currency context
+    if(theCurrency){
+        double rounding = ucurr_getRoundingIncrementForUsage(theCurrency, fCurrencyUsage, ec);
+        int32_t frac = ucurr_getDefaultFractionDigitsForUsage(theCurrency, fCurrencyUsage, ec);
+
+        if (U_SUCCESS(*ec)) {
+            setRoundingIncrement(rounding);
+            setMinimumFractionDigits(frac);
+            setMaximumFractionDigits(frac);
+        }
+    }
+}
+
+UCurrencyUsage DecimalFormat::getCurrencyUsage() const {
+    return fCurrencyUsage;
+}
+
 // Deprecated variant with no UErrorCode parameter
 void DecimalFormat::setCurrency(const UChar* theCurrency) {
     UErrorCode ec = U_ZERO_ERROR;
@@ -5274,7 +5345,7 @@ DecimalFormat::deleteHashForAffix(Hashtable*& table)
     if ( table == NULL ) {
         return;
     }
-    int32_t pos = -1;
+    int32_t pos = UHASH_FIRST;
     const UHashElement* element = NULL;
     while ( (element = table->nextElement(pos)) != NULL ) {
         const UHashTok valueTok = element->value;
@@ -5293,7 +5364,7 @@ DecimalFormat::deleteHashForAffixPattern()
     if ( fAffixPatternsForCurrency == NULL ) {
         return;
     }
-    int32_t pos = -1;
+    int32_t pos = UHASH_FIRST;
     const UHashElement* element = NULL;
     while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) {
         const UHashTok valueTok = element->value;
@@ -5312,7 +5383,7 @@ DecimalFormat::copyHashForAffixPattern(const Hashtable* source,
     if ( U_FAILURE(status) ) {
         return;
     }
-    int32_t pos = -1;
+    int32_t pos = UHASH_FIRST;
     const UHashElement* element = NULL;
     if ( source ) {
         while ( (element = source->nextElement(pos)) != NULL ) {
@@ -5451,6 +5522,7 @@ DecimalFormat& DecimalFormat::setAttribute( UNumberFormatAttribute attr,
     /* These are stored in fBoolFlags */
     case UNUM_PARSE_NO_EXPONENT:
     case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS:
+    case UNUM_PARSE_DECIMAL_MARK_REQUIRED:
       if(!fBoolFlags.isValidValue(newValue)) {
           status = U_ILLEGAL_ARGUMENT_ERROR;
       } else {
@@ -5462,6 +5534,9 @@ DecimalFormat& DecimalFormat::setAttribute( UNumberFormatAttribute attr,
         fScale = newValue;
         break;
 
+    case UNUM_CURRENCY_USAGE:
+        setCurrencyUsage((UCurrencyUsage)newValue, &status);
+
     default:
       status = U_UNSUPPORTED_ERROR;
       break;
@@ -5535,11 +5610,15 @@ int32_t DecimalFormat::getAttribute( UNumberFormatAttribute attr,
     /* These are stored in fBoolFlags */
     case UNUM_PARSE_NO_EXPONENT:
     case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS:
+    case UNUM_PARSE_DECIMAL_MARK_REQUIRED:
       return fBoolFlags.get(attr);
 
     case UNUM_SCALE:
         return fScale;
 
+    case UNUM_CURRENCY_USAGE:
+        return fCurrencyUsage;
+
     default:
         status = U_UNSUPPORTED_ERROR;
         break;
@@ -5564,7 +5643,7 @@ DecimalFormat::copyHashForAffix(const Hashtable* source,
     if ( U_FAILURE(status) ) {
         return;
     }
-    int32_t pos = -1;
+    int32_t pos = UHASH_FIRST;
     const UHashElement* element = NULL;
     if ( source ) {
         while ( (element = source->nextElement(pos)) != NULL ) {