ICU-20562 Adding toDecimalNumber method to C++ FormattedNumber.
authorShane Carr <shane@unicode.org>
Wed, 14 Aug 2019 22:47:41 +0000 (15:47 -0700)
committerShane F. Carr <shane@unicode.org>
Wed, 14 Aug 2019 23:59:09 +0000 (16:59 -0700)
icu4c/source/i18n/number_decnum.h
icu4c/source/i18n/number_output.cpp
icu4c/source/i18n/number_utils.cpp
icu4c/source/i18n/unicode/numberformatter.h
icu4c/source/i18n/unicode/unumberformatter.h
icu4c/source/test/intltest/numbertest.h
icu4c/source/test/intltest/numbertest_api.cpp

index a7793470b55695586ec0e5e4691ea9d09d4a5d33..0c7399dbddd43b539b0183e0245f5f2953d8d629 100644 (file)
@@ -55,6 +55,8 @@ class U_I18N_API DecNum : public UMemory {
 
     bool isZero() const;
 
+    void toString(ByteSink& output, UErrorCode& status) const;
+
     inline const decNumber* getRawDecNumber() const {
         return fData.getAlias();
     }
index 8804916ace7a3cabad895275a359845ad911d4b1..e2f069139a4b47f8d3c704d34d4117338063cfdc 100644 (file)
@@ -9,6 +9,7 @@
 #include "number_utypes.h"
 #include "util.h"
 #include "number_decimalquantity.h"
+#include "number_decnum.h"
 
 U_NAMESPACE_BEGIN
 namespace number {
@@ -28,6 +29,13 @@ void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErr
     getAllFieldPositionsImpl(fpih, status);
 }
 
+void FormattedNumber::toDecimalNumber(ByteSink& sink, UErrorCode& status) const {
+    UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG)
+    impl::DecNum decnum;
+    fData->quantity.toDecNum(decnum, status);
+    decnum.toString(sink, status);
+}
+
 void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih,
                                                UErrorCode& status) const {
     UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG)
index 0983b7b0726ec2c3a6be8fbbae111cf9994ffd01..91d7f335cd82d3daa91b4abddffdd115e486b7fc 100644 (file)
@@ -252,4 +252,15 @@ bool DecNum::isZero() const {
     return decNumberIsZero(fData.getAlias());
 }
 
+void DecNum::toString(ByteSink& output, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    // "string must be at least dn->digits+14 characters long"
+    int32_t minCapacity = fData.getAlias()->digits + 14;
+    MaybeStackArray<char, 30> buffer(minCapacity);
+    uprv_decNumberToString(fData, buffer.getAlias());
+    output.Append(buffer.getAlias(), static_cast<int32_t>(uprv_strlen(buffer.getAlias())));
+}
+
 #endif /* #if !UCONFIG_NO_FORMATTING */
index e5b85fe39544853eecc8464ee7ceb2bc99bc3bb4..f93627f8afc27c46ac04e0820cdbad86d55c2349 100644 (file)
@@ -11,8 +11,9 @@
 #if !UCONFIG_NO_FORMATTING
 
 #include "unicode/appendable.h"
-#include "unicode/dcfmtsym.h"
+#include "unicode/bytestream.h"
 #include "unicode/currunit.h"
+#include "unicode/dcfmtsym.h"
 #include "unicode/fieldpos.h"
 #include "unicode/formattedvalue.h"
 #include "unicode/fpositer.h"
@@ -2520,6 +2521,27 @@ class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
      */
     void getAllFieldPositions(FieldPositionIterator &iterator, UErrorCode &status) const;
 
+    /**
+     * Export the formatted number as a "numeric string" conforming to the
+     * syntax defined in the Decimal Arithmetic Specification, available at
+     * http://speleotrove.com/decimal
+     * 
+     * This endpoint is useful for obtaining the exact number being printed
+     * after scaling and rounding have been applied by the number formatter.
+     *
+     * Example call site:
+     *
+     *     auto decimalNumber = fn.toDecimalNumber<std::string>(status);
+     *
+     * @tparam StringClass A string class compatible with StringByteSink;
+     *         for example, std::string.
+     * @param status Set if an error occurs.
+     * @return A StringClass containing the numeric string.
+     * @draft ICU 65
+     */
+    template<typename StringClass>
+    inline StringClass toDecimalNumber(UErrorCode& status) const;
+
 #ifndef U_HIDE_INTERNAL_API
 
     /**
@@ -2553,6 +2575,9 @@ class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
     explicit FormattedNumber(UErrorCode errorCode)
         : fData(nullptr), fErrorCode(errorCode) {}
 
+    // TODO(ICU-20775): Propose this as API.
+    void toDecimalNumber(ByteSink& sink, UErrorCode& status) const;
+
     // To give LocalizedNumberFormatter format methods access to this class's constructor:
     friend class LocalizedNumberFormatter;
 
@@ -2560,6 +2585,16 @@ class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
     friend struct impl::UFormattedNumberImpl;
 };
 
+#ifndef U_HIDE_DRAFT_API
+template<typename StringClass>
+StringClass FormattedNumber::toDecimalNumber(UErrorCode& status) const {
+    StringClass result;
+    StringByteSink<StringClass> sink(&result);
+    toDecimalNumber(sink, status);
+    return result;
+};
+#endif  // U_HIDE_DRAFT_API
+
 /**
  * See the main description in numberformatter.h for documentation and examples.
  *
index b38763c6b8e79f598d744e3218b7dcfea1173d64..080e567a81cf758868cdf187586fa8c916f39d60 100644 (file)
@@ -650,6 +650,13 @@ unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPosition
                                  UErrorCode* ec);
 
 
+// TODO(ICU-20775): Propose this as API.
+// NOTE: This is not currently implemented.
+// U_DRAFT int32_t U_EXPORT2
+// unumf_resultToDecimalNumber(const UFormattedNumber* uresult, char* buffer, int32_t bufferCapacity,
+//                             UErrorCode* ec);
+
+
 /**
  * Releases the UNumberFormatter created by unumf_openForSkeletonAndLocale().
  *
index b0e7f9f263dd97f5f2946f6acd81dc466b62ec27..7f5bcdf4a8ef368ea733a0e2b92423ce5f4b2c1b 100644 (file)
@@ -82,6 +82,7 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
     void copyMove();
     void localPointerCAPI();
     void toObject();
+    void toDecimalNumber();
 
     void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
 
index cfc10abddcd3c32085190ac60dd4ab60dee2deef..57b307935da815b8cbdb0d8b883527d52f4a0783 100644 (file)
@@ -104,6 +104,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
         TESTCASE_AUTO(copyMove);
         TESTCASE_AUTO(localPointerCAPI);
         TESTCASE_AUTO(toObject);
+        TESTCASE_AUTO(toDecimalNumber);
     TESTCASE_AUTO_END;
 }
 
@@ -3033,6 +3034,18 @@ void NumberFormatterApiTest::toObject() {
     }
 }
 
+void NumberFormatterApiTest::toDecimalNumber() {
+    IcuTestErrorCode status(*this, "toDecimalNumber");
+    FormattedNumber fn = NumberFormatter::withLocale("bn-BD")
+        .scale(Scale::powerOfTen(2))
+        .precision(Precision::maxSignificantDigits(5))
+        .formatDouble(9.87654321e12, status);
+    assertEquals("Should have expected localized string result",
+        u"৯৮,৭৬,৫০,০০,০০,০০,০০০", fn.toString(status));
+    assertEquals(u"Should have expected toDecimalNumber string result",
+        "9.8765E+14", fn.toDecimalNumber<std::string>(status).c_str());
+}
+
 
 void NumberFormatterApiTest::assertFormatDescending(const char16_t* umessage, const char16_t* uskeleton,
                                                     const UnlocalizedNumberFormatter& f, Locale locale,