Uploaded image for project: 'ICU'
  1. ICU-13731

Add tests for "Fallback symbols displayed in formatted currency output when no currency is set in API" and for "Rounding behavior when no currency is set in API"

    Details

    • Time Needed:
      Minutes
    • tracCc:
      pedberg
    • tracOwner:
      shane
    • tracProject:
      all
    • tracReporter:
      shane
    • tracStatus:
      accepted

      Description

      This behavior difference was discovered between ICU4C and ICU4J.

      Sample code, ICU4C:

      #include "common.h"
      
      int main() {
        ErrorCode status;
        {
          LocalPointer<DecimalFormat> df(dynamic_cast<DecimalFormat*>(
            NumberFormat::createInstance("en", UNUM_CURRENCY, status)));
          UnicodeString result;
          df->format(1.23, result, status);
          std::cout << "symbol: " << result << std::endl;
        }
        {
          LocalPointer<DecimalFormat> df(dynamic_cast<DecimalFormat*>(
            NumberFormat::createInstance("en", UNUM_CURRENCY_ISO, status)));
          UnicodeString result;
          df->format(1.23, result, status);
          std::cout << "iso_code: " << result << std::endl;
        }
        {
          LocalPointer<DecimalFormat> df(dynamic_cast<DecimalFormat*>(
            NumberFormat::createInstance("en", UNUM_CURRENCY_PLURAL, status)));
          UnicodeString result;
          df->format(1.23, result, status);
          std::cout << "plural: " << result << std::endl;
        }
        {
          LocalPointer<DecimalFormat> df(dynamic_cast<DecimalFormat*>(
            NumberFormat::createInstance("en", UNUM_CURRENCY, status)));
          UnicodeString currencyCode = UnicodeString(df->getCurrency());
          std::cout << "currency: \"" << currencyCode << "\"" << std::endl;
        }
        std::cout << u_errorName(status) << std::endl;
        u_cleanup();
        return 0;
      }
      

      ICU4J:

      import java.text.ParsePosition;
      
      import com.ibm.icu.text.*;
      import com.ibm.icu.util.*;
      
      public class NoCurrencyBehavior {
        public static void main(String[] args) {
          DecimalFormatSymbols EN = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
          {
            DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH, NumberFormat.CURRENCYSTYLE);
            System.out.println("symbol: " + df.format(1.23));
          }
          {
            DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH, NumberFormat.ISOCURRENCYSTYLE);
            System.out.println("iso_code: " + df.format(1.23));
          }
          {
            DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH, NumberFormat.PLURALCURRENCYSTYLE);
            System.out.println("plural: " + df.format(1.23));
          }
          {
            DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH, NumberFormat.CURRENCYSTYLE);
            System.out.print("currency: " + df.getCurrency());
          }
        }
      }
      

      Consensus opinion: we should use the unknown currency "XXX", like ICU4J and JDK are already doing in the ISO code format, with the following output:

      XXX 1.23
      XXX 1.23
      1.23 (unknown currency)
      currency: XXX
      

      The plural long-name string, "unknown currency", comes from locale data.

      Follow up with CLDR to add the currency symbol.

      In addition, one more behavior difference was discovered.

      ICU4C sample code:

      #include "common.h"
      
      int main() {
        ErrorCode status;
        const UChar pattern[] = {0xA4, u'0', u'.', u'#', u'#', 0};
        const UChar XXX[] = {u'X', u'X', u'X', 0};
        {
          DecimalFormat df(pattern, {"en", status}, status);
          UnicodeString result;
          df.format(1.1, result, status);
          std::cout << result << std::endl;
        }
        {
          DecimalFormat df(pattern, {"en", status}, status);
          df.setCurrency(XXX, status);
          UnicodeString result;
          df.format(1.1, result, status);
          std::cout << result << std::endl;
        }
        {
          LocalPointer<DecimalFormat> df(dynamic_cast<DecimalFormat*>(
            NumberFormat::createCurrencyInstance("en", status)));
          UnicodeString result;
          df->format(1.1, result, status);
          std::cout << result << std::endl;
        }
        std::cout << u_errorName(status) << std::endl;
        u_cleanup();
        return 0;
      }
      

      ICU4J:

      import com.ibm.icu.text.*;
      import com.ibm.icu.util.*;
      
      public class RoundingNoCurrency {
        public static void main(String[] args) {
          {
            DecimalFormat df = new DecimalFormat("\u00A40.##", DecimalFormatSymbols.getInstance(ULocale.ENGLISH));
            System.out.println(df.format(1.1));
          }
          {
            DecimalFormat df = new DecimalFormat("\u00A40.##", DecimalFormatSymbols.getInstance(ULocale.ENGLISH));
            df.setCurrency(Currency.getInstance("XXX"));
            System.out.println(df.format(1.1));
          }
          {
            DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(ULocale.ENGLISH);
            System.out.println(df.format(1.1));
          }
        }
      }
      

      Consensus opinion: Since we should apply the default currency when no currency is set, as stated in row 10, we should also apply XXX's rounding rules, which are a fixed two fraction digits.

      Desired output:

      XXX 1.10
      XXX 1.10
      1.10 (unknown currency)
      

        Attachments

          Issue links

            Activity

              People

              • Assignee:
                shane Shane Carr
                Reporter:
                shane Shane Carr
              • Votes:
                0 Vote for this issue
                Watchers:
                5 Start watching this issue

                Dates

                • Created:
                  Updated:
                  tracCreated: