, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
+ , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
+ , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
+ , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
+ , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
+ , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
+ , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
+ , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
setDecimalFormatSymbols(*rhs.getDecimalFormatSymbols());
init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status);
setDefaultRuleSet(rhs.getDefaultRuleSetName(), status);
+ setRoundingMode(rhs.getRoundingMode());
capitalizationInfoSet = rhs.capitalizationInfoSet;
capitalizationForUIListMenu = rhs.capitalizationForUIListMenu;
int32_t startPos = toAppendTo.length();
UErrorCode status = U_ZERO_ERROR;
if (defaultRuleSet) {
- defaultRuleSet->format(number, toAppendTo, toAppendTo.length(), 0, status);
+ format(number, *defaultRuleSet, toAppendTo, status);
}
return adjustForCapitalizationContext(startPos, toAppendTo, status);
}
} else {
NFRuleSet *rs = findRuleSet(ruleSetName, status);
if (rs) {
- int32_t startPos = toAppendTo.length();
- rs->format(number, toAppendTo, toAppendTo.length(), 0, status);
- adjustForCapitalizationContext(startPos, toAppendTo, status);
+ format(number, *rs, toAppendTo, status);
}
}
}
return toAppendTo;
}
+void
+RuleBasedNumberFormat::format(double number,
+ NFRuleSet& rs,
+ UnicodeString& toAppendTo,
+ UErrorCode& status) const
+{
+ int32_t startPos = toAppendTo.length();
+ if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary && !uprv_isNaN(number) && !uprv_isInfinite(number)) {
+ DigitList digitList;
+ digitList.set(number);
+ digitList.setRoundingMode(getRoundingMode());
+ digitList.roundFixedPoint(getMaximumFractionDigits());
+ number = digitList.getDouble();
+ }
+ rs.format(number, toAppendTo, toAppendTo.length(), 0, status);
+ adjustForCapitalizationContext(startPos, toAppendTo, status);
+}
+
/**
* Bottleneck through which all the public format() methods
* that take a long pass. By the time we get here, we know
return new PluralFormat(locale, pluralType, pattern, status);
}
+/**
+ * Get the rounding mode.
+ * @return A rounding mode
+ */
+DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const {
+ return roundingMode;
+}
+
+/**
+ * Set the rounding mode. This has no effect unless the rounding
+ * increment is greater than zero.
+ * @param roundingMode A rounding mode
+ */
+void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode) {
+ this->roundingMode = roundingMode;
+}
+
U_NAMESPACE_END
/* U_HAVE_RBNF */
#define U_HAVE_RBNF 1
#include "unicode/dcfmtsym.h"
+#include "unicode/decimfmt.h"
#include "unicode/fmtable.h"
#include "unicode/locid.h"
#include "unicode/numfmt.h"
*/
virtual void setContext(UDisplayContext value, UErrorCode& status);
+#ifndef U_HIDE_DRAFT_API
+ /**
+ * Get the rounding mode.
+ * @return A rounding mode
+ * @draft ICU 60
+ */
+ virtual DecimalFormat::ERoundingMode getRoundingMode(void) const;
+
+ /**
+ * Set the rounding mode.
+ * @param roundingMode A rounding mode
+ * @draft ICU 60
+ */
+ virtual void setRoundingMode(DecimalFormat::ERoundingMode roundingMode);
+#endif /* U_HIDE_DRAFT_API */
+
public:
/**
* ICU "poor man's RTTI", returns a UClassID for this class.
void dispose();
void stripWhitespace(UnicodeString& src);
void initDefaultRuleSet();
- void format(double number, NFRuleSet& ruleSet);
NFRuleSet* findRuleSet(const UnicodeString& name, UErrorCode& status) const;
/* friend access */
PluralFormat *createPluralFormat(UPluralType pluralType, const UnicodeString &pattern, UErrorCode& status) const;
UnicodeString& adjustForCapitalizationContext(int32_t startPos, UnicodeString& currentResult, UErrorCode& status) const;
UnicodeString& format(int64_t number, NFRuleSet *ruleSet, UnicodeString& toAppendTo, UErrorCode& status) const;
+ void format(double number, NFRuleSet& rs, UnicodeString& toAppendTo, UErrorCode& status) const;
private:
NFRuleSet **ruleSets;
DecimalFormatSymbols* decimalFormatSymbols;
NFRule *defaultInfinityRule;
NFRule *defaultNaNRule;
+ DecimalFormat::ERoundingMode roundingMode;
UBool lenient;
UnicodeString* lenientParseRules;
LocalizationInfo* localizations;
unum_getAttribute(const UNumberFormat* fmt,
UNumberFormatAttribute attr)
{
- const NumberFormat* nf = reinterpret_cast<const NumberFormat*>(fmt);
- if ( attr == UNUM_LENIENT_PARSE ) {
- // Supported for all subclasses
- return nf->isLenient();
- }
+ const NumberFormat* nf = reinterpret_cast<const NumberFormat*>(fmt);
+ if (attr == UNUM_LENIENT_PARSE) {
+ // Supported for all subclasses
+ return nf->isLenient();
+ }
+ else if (attr == UNUM_MAX_INTEGER_DIGITS) {
+ return nf->getMaximumIntegerDigits();
+ }
+ else if (attr == UNUM_MIN_INTEGER_DIGITS) {
+ return nf->getMinimumIntegerDigits();
+ }
+ else if (attr == UNUM_INTEGER_DIGITS) {
+ // TODO: what should this return?
+ return nf->getMinimumIntegerDigits();
+ }
+ else if (attr == UNUM_MAX_FRACTION_DIGITS) {
+ return nf->getMaximumFractionDigits();
+ }
+ else if (attr == UNUM_MIN_FRACTION_DIGITS) {
+ return nf->getMinimumFractionDigits();
+ }
+ else if (attr == UNUM_FRACTION_DIGITS) {
+ // TODO: what should this return?
+ return nf->getMinimumFractionDigits();
+ }
- // The remaining attributea are only supported for DecimalFormat
- const DecimalFormat* df = dynamic_cast<const DecimalFormat*>(nf);
- if (df != NULL) {
- UErrorCode ignoredStatus = U_ZERO_ERROR;
- return df->getAttribute( attr, ignoredStatus );
- }
+ // DecimalFormat specific attributes
+ const DecimalFormat* df = dynamic_cast<const DecimalFormat*>(nf);
+ if (df != NULL) {
+ UErrorCode ignoredStatus = U_ZERO_ERROR;
+ return df->getAttribute(attr, ignoredStatus);
+ }
- return -1;
+ // RuleBasedNumberFormat specific attributes
+ const RuleBasedNumberFormat* rbnf = dynamic_cast<const RuleBasedNumberFormat*>(nf);
+ if (rbnf != NULL) {
+ if (attr == UNUM_ROUNDING_MODE) {
+ return rbnf->getRoundingMode();
+ }
+ }
+
+ return -1;
}
U_CAPI void U_EXPORT2
UNumberFormatAttribute attr,
int32_t newValue)
{
- NumberFormat* nf = reinterpret_cast<NumberFormat*>(fmt);
- if ( attr == UNUM_LENIENT_PARSE ) {
- // Supported for all subclasses
- // keep this here as the class may not be a DecimalFormat
- return nf->setLenient(newValue != 0);
- }
- // The remaining attributea are only supported for DecimalFormat
- DecimalFormat* df = dynamic_cast<DecimalFormat*>(nf);
- if (df != NULL) {
- UErrorCode ignoredStatus = U_ZERO_ERROR;
- df->setAttribute(attr, newValue, ignoredStatus);
- }
+ NumberFormat* nf = reinterpret_cast<NumberFormat*>(fmt);
+ if (attr == UNUM_LENIENT_PARSE) {
+ // Supported for all subclasses
+ // keep this here as the class may not be a DecimalFormat
+ return nf->setLenient(newValue != 0);
+ }
+ else if (attr == UNUM_MAX_INTEGER_DIGITS) {
+ return nf->setMaximumIntegerDigits(newValue);
+ }
+ else if (attr == UNUM_MIN_INTEGER_DIGITS) {
+ return nf->setMinimumIntegerDigits(newValue);
+ }
+ else if (attr == UNUM_INTEGER_DIGITS) {
+ nf->setMinimumIntegerDigits(newValue);
+ return nf->setMaximumIntegerDigits(newValue);
+ }
+ else if (attr == UNUM_MAX_FRACTION_DIGITS) {
+ return nf->setMaximumFractionDigits(newValue);
+ }
+ else if (attr == UNUM_MIN_FRACTION_DIGITS) {
+ return nf->setMinimumFractionDigits(newValue);
+ }
+ else if (attr == UNUM_FRACTION_DIGITS) {
+ nf->setMinimumFractionDigits(newValue);
+ return nf->setMaximumFractionDigits(newValue);
+ }
+
+ // DecimalFormat specific attributes
+ DecimalFormat* df = dynamic_cast<DecimalFormat*>(nf);
+ if (df != NULL) {
+ UErrorCode ignoredStatus = U_ZERO_ERROR;
+ df->setAttribute(attr, newValue, ignoredStatus);
+ }
+
+ // RuleBasedNumberFormat specific attributes
+ RuleBasedNumberFormat* rbnf = dynamic_cast<RuleBasedNumberFormat*>(nf);
+ if (rbnf != NULL) {
+ if (attr == UNUM_ROUNDING_MODE) {
+ return rbnf->setRoundingMode((DecimalFormat::ERoundingMode)newValue);
+ }
+ }
}
U_CAPI double U_EXPORT2
static void TestVariousStylesAndAttributes(void);
static void TestParseCurrPatternWithDecStyle(void);
static void TestFormatForFields(void);
+static void TestRBNFRounding(void);
#define TESTCASE(x) addTest(root, &x, "tsformat/cnumtst/" #x)
TESTCASE(TestCurrencyRegression);
TESTCASE(TestTextAttributeCrash);
TESTCASE(TestRBNFFormat);
+ TESTCASE(TestRBNFRounding);
TESTCASE(TestNBSPInPattern);
TESTCASE(TestInt64Parse);
TESTCASE(TestParseZero);
}
}
+static void TestRBNFRounding() {
+ UChar fmtbuf[FORMAT_BUF_CAPACITY];
+ UChar expectedBuf[FORMAT_BUF_CAPACITY];
+ int32_t len;
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* fmt = unum_open(UNUM_SPELLOUT, NULL, 0, "en_US", NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err_status(status, "unable to open spellout -> %s\n", u_errorName(status));
+ return;
+ }
+ len = unum_formatDouble(fmt, 10.123456789, fmtbuf, FORMAT_BUF_CAPACITY, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err_status(status, "unum_formatDouble 10.123456789 failed with %s\n", u_errorName(status));
+ }
+ u_uastrcpy(expectedBuf, "ten point one two three four five six seven eight nine");
+ if (u_strcmp(expectedBuf, fmtbuf) != 0) {
+ log_err("Wrong result for unrounded value\n");
+ }
+ unum_setAttribute(fmt, UNUM_MAX_FRACTION_DIGITS, 3);
+ if (unum_getAttribute(fmt, UNUM_MAX_FRACTION_DIGITS) != 3) {
+ log_err("UNUM_MAX_FRACTION_DIGITS was incorrectly ignored -> %d\n", unum_getAttribute(fmt, UNUM_MAX_FRACTION_DIGITS));
+ }
+ if (unum_getAttribute(fmt, UNUM_ROUNDING_MODE) != UNUM_ROUND_UNNECESSARY) {
+ log_err("UNUM_ROUNDING_MODE was set -> %d\n", unum_getAttribute(fmt, UNUM_ROUNDING_MODE));
+ }
+ unum_setAttribute(fmt, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
+ if (unum_getAttribute(fmt, UNUM_ROUNDING_MODE) != UNUM_ROUND_HALFUP) {
+ log_err("UNUM_ROUNDING_MODE was not set -> %d\n", unum_getAttribute(fmt, UNUM_ROUNDING_MODE));
+ }
+ len = unum_formatDouble(fmt, 10.123456789, fmtbuf, FORMAT_BUF_CAPACITY, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err_status(status, "unum_formatDouble 10.123456789 failed with %s\n", u_errorName(status));
+ }
+ u_uastrcpy(expectedBuf, "ten point one two three");
+ if (u_strcmp(expectedBuf, fmtbuf) != 0) {
+ char temp[512];
+ u_austrcpy(temp, fmtbuf);
+ log_err("Wrong result for rounded value. Got: %s\n", temp);
+ }
+ unum_close(fmt);
+}
+
static void TestCurrencyRegression(void) {
/*
I've found a case where unum_parseDoubleCurrency is not doing what I