ICU-21493 Add more rounding modes in ICU4C
See #1575
diff --git a/docs/userguide/format_parse/numbers/rounding-modes.md b/docs/userguide/format_parse/numbers/rounding-modes.md
index cd02b8f..7ff34f7 100644
--- a/docs/userguide/format_parse/numbers/rounding-modes.md
+++ b/docs/userguide/format_parse/numbers/rounding-modes.md
@@ -30,50 +30,52 @@
This chart shows the values -2.0 through 2.0 in increments of 0.1, and shows the
resulting ICU format when formatted with no decimal digits.
-| # | CEILING | FLOOR | DOWN | UP | HALFEVEN | HALFDOWN | HALFUP | # |
-|------|---------|-------|------|----|----------|----------|--------|------|
-| -2.0 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2.0 |
-| -1.9 | -1 | -2 | -1 | -2 | -2 | -2 | -2 | -1.9 |
-| -1.8 | -1 | -2 | -1 | -2 | -2 | -2 | -2 | -1.8 |
-| -1.7 | -1 | -2 | -1 | -2 | -2 | -2 | -2 | -1.7 |
-| -1.6 | -1 | -2 | -1 | -2 | -2 | -2 | -2 | -1.6 |
-| -1.5 | -1 | -2 | -1 | -2 | -2 | -1 | -2 | -1.5 |
-| -1.4 | -1 | -2 | -1 | -2 | -1 | -1 | -1 | -1.4 |
-| -1.3 | -1 | -2 | -1 | -2 | -1 | -1 | -1 | -1.3 |
-| -1.2 | -1 | -2 | -1 | -2 | -1 | -1 | -1 | -1.2 |
-| -1.1 | -1 | -2 | -1 | -2 | -1 | -1 | -1 | -1.1 |
-| -1.0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1.0 |
-| -0.9 | -0 | -1 | -0 | -1 | -1 | -1 | -1 | -0.9 |
-| -0.8 | -0 | -1 | -0 | -1 | -1 | -1 | -1 | -0.8 |
-| -0.7 | -0 | -1 | -0 | -1 | -1 | -1 | -1 | -0.7 |
-| -0.6 | -0 | -1 | -0 | -1 | -1 | -1 | -1 | -0.6 |
-| -0.5 | -0 | -1 | -0 | -1 | -0 | -0 | -1 | -0.5 |
-| -0.4 | -0 | -1 | -0 | -1 | -0 | -0 | -0 | -0.4 |
-| -0.3 | -0 | -1 | -0 | -1 | -0 | -0 | -0 | -0.3 |
-| -0.2 | -0 | -1 | -0 | -1 | -0 | -0 | -0 | -0.2 |
-| -0.1 | -0 | -1 | -0 | -1 | -0 | -0 | -0 | -0.1 |
-| 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 |
-| 0.1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0.1 |
-| 0.2 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0.2 |
-| 0.3 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0.3 |
-| 0.4 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0.4 |
-| 0.5 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0.5 |
-| 0.6 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0.6 |
-| 0.7 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0.7 |
-| 0.8 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0.8 |
-| 0.9 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0.9 |
-| 1.0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1.0 |
-| 1.1 | 2 | 1 | 1 | 2 | 1 | 1 | 1 | 1.1 |
-| 1.2 | 2 | 1 | 1 | 2 | 1 | 1 | 1 | 1.2 |
-| 1.3 | 2 | 1 | 1 | 2 | 1 | 1 | 1 | 1.3 |
-| 1.4 | 2 | 1 | 1 | 2 | 1 | 1 | 1 | 1.4 |
-| 1.5 | 2 | 1 | 1 | 2 | 2 | 1 | 2 | 1.5 |
-| 1.6 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 1.6 |
-| 1.7 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 1.7 |
-| 1.8 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 1.8 |
-| 1.9 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 1.9 |
-| 2.0 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2.0 |
-| # | CEILING | FLOOR | DOWN | UP | HALFEVEN | HALFDOWN | HALFUP | # |
+*Note: Some of the options below are not available in ICU4J because we use the JDK RoundingMode.*
+
+| # | CEILING | FLOOR | DOWN | UP | HALFEVEN | HALFODD | HALFCEILING | HALFFLOOR | HALFDOWN | HALFUP | # |
+|------|---------|-------|------|----|----------|---------|-------------|-----------|----------|--------|------|
+| -2.0 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -2.0 |
+| -1.9 | -1 | -2 | -1 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -1.9 |
+| -1.8 | -1 | -2 | -1 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -1.8 |
+| -1.7 | -1 | -2 | -1 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -1.7 |
+| -1.6 | -1 | -2 | -1 | -2 | -2 | -2 | -2 | -2 | -2 | -2 | -1.6 |
+| -1.5 | -1 | -2 | -1 | -2 | -2 | -1 | -1 | -2 | -1 | -2 | -1.5 |
+| -1.4 | -1 | -2 | -1 | -2 | -1 | -1 | -1 | -1 | -1 | -1 | -1.4 |
+| -1.3 | -1 | -2 | -1 | -2 | -1 | -1 | -1 | -1 | -1 | -1 | -1.3 |
+| -1.2 | -1 | -2 | -1 | -2 | -1 | -1 | -1 | -1 | -1 | -1 | -1.2 |
+| -1.1 | -1 | -2 | -1 | -2 | -1 | -1 | -1 | -1 | -1 | -1 | -1.1 |
+| -1.0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1.0 |
+| -0.9 | -0 | -1 | -0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -0.9 |
+| -0.8 | -0 | -1 | -0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -0.8 |
+| -0.7 | -0 | -1 | -0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -0.7 |
+| -0.6 | -0 | -1 | -0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -0.6 |
+| -0.5 | -0 | -1 | -0 | -1 | -0 | -1 | -0 | -1 | -0 | -1 | -0.5 |
+| -0.4 | -0 | -1 | -0 | -1 | -0 | -0 | -0 | -0 | -0 | -0 | -0.4 |
+| -0.3 | -0 | -1 | -0 | -1 | -0 | -0 | -0 | -0 | -0 | -0 | -0.3 |
+| -0.2 | -0 | -1 | -0 | -1 | -0 | -0 | -0 | -0 | -0 | -0 | -0.2 |
+| -0.1 | -0 | -1 | -0 | -1 | -0 | -0 | -0 | -0 | -0 | -0 | -0.1 |
+| 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 |
+| 0.1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.1 |
+| 0.2 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.2 |
+| 0.3 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.3 |
+| 0.4 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.4 |
+| 0.5 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0.5 |
+| 0.6 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0.6 |
+| 0.7 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0.7 |
+| 0.8 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0.8 |
+| 0.9 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0.9 |
+| 1.0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1.0 |
+| 1.1 | 2 | 1 | 1 | 2 | 1 | 1 | 1 | 1 | 1 | 1 | 1.1 |
+| 1.2 | 2 | 1 | 1 | 2 | 1 | 1 | 1 | 1 | 1 | 1 | 1.2 |
+| 1.3 | 2 | 1 | 1 | 2 | 1 | 1 | 1 | 1 | 1 | 1 | 1.3 |
+| 1.4 | 2 | 1 | 1 | 2 | 1 | 1 | 1 | 1 | 1 | 1 | 1.4 |
+| 1.5 | 2 | 1 | 1 | 2 | 2 | 1 | 2 | 1 | 1 | 2 | 1.5 |
+| 1.6 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 1.6 |
+| 1.7 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 1.7 |
+| 1.8 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 1.8 |
+| 1.9 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 1.9 |
+| 2.0 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2.0 |
+| # | CEILING | FLOOR | DOWN | UP | HALFEVEN | HALFODD | HALFCEILING | HALFFLOOR | HALFDOWN | HALFUP | # |
### Half Even
@@ -84,6 +86,10 @@
Also known as ties-to-even, round-to-nearest, RN or RNE.
+### Half Odd
+
+Similar to Half Even, but rounds ties to the nearest odd number instead of even number.
+
### Half Down
Values exactly on the 0.5 (half) mark are rounded down (next smaller absolute
@@ -106,6 +112,16 @@
All values are rounded towards the next greater absolute value (away from zero).
+### Half Ceiling
+
+Values exactly on the 0.5 (half) mark are rounded toward positive infinity (+∞).
+
+This is the default rounding mode in ECMAScript. In CSS, it is known as "nearest".
+
+### Half Floor
+
+Values exactly on the 0.5 (half) mark are rounded towards negative infinity (-∞).
+
### Ceiling
All values are rounded towards positive infinity (+∞). Also known as RI for
diff --git a/icu4c/source/i18n/number_roundingutils.h b/icu4c/source/i18n/number_roundingutils.h
index 11e70ab..06fadd2 100644
--- a/icu4c/source/i18n/number_roundingutils.h
+++ b/icu4c/source/i18n/number_roundingutils.h
@@ -104,6 +104,45 @@
}
break;
+ case RoundingMode::UNUM_ROUND_HALF_ODD:
+ switch (section) {
+ case SECTION_MIDPOINT:
+ return !isEven;
+ case SECTION_LOWER:
+ return true;
+ case SECTION_UPPER:
+ return false;
+ default:
+ break;
+ }
+ break;
+
+ case RoundingMode::UNUM_ROUND_HALF_CEILING:
+ switch (section) {
+ case SECTION_MIDPOINT:
+ return isNegative;
+ case SECTION_LOWER:
+ return true;
+ case SECTION_UPPER:
+ return false;
+ default:
+ break;
+ }
+ break;
+
+ case RoundingMode::UNUM_ROUND_HALF_FLOOR:
+ switch (section) {
+ case SECTION_MIDPOINT:
+ return !isNegative;
+ case SECTION_LOWER:
+ return true;
+ case SECTION_UPPER:
+ return false;
+ default:
+ break;
+ }
+ break;
+
default:
break;
}
diff --git a/icu4c/source/i18n/number_skeletons.cpp b/icu4c/source/i18n/number_skeletons.cpp
index a07725b..0110e95e 100644
--- a/icu4c/source/i18n/number_skeletons.cpp
+++ b/icu4c/source/i18n/number_skeletons.cpp
@@ -68,6 +68,9 @@
b.add(u"rounding-mode-down", STEM_ROUNDING_MODE_DOWN, status);
b.add(u"rounding-mode-up", STEM_ROUNDING_MODE_UP, status);
b.add(u"rounding-mode-half-even", STEM_ROUNDING_MODE_HALF_EVEN, status);
+ b.add(u"rounding-mode-half-odd", STEM_ROUNDING_MODE_HALF_ODD, status);
+ b.add(u"rounding-mode-half-ceiling", STEM_ROUNDING_MODE_HALF_CEILING, status);
+ b.add(u"rounding-mode-half-floor", STEM_ROUNDING_MODE_HALF_FLOOR, status);
b.add(u"rounding-mode-half-down", STEM_ROUNDING_MODE_HALF_DOWN, status);
b.add(u"rounding-mode-half-up", STEM_ROUNDING_MODE_HALF_UP, status);
b.add(u"rounding-mode-unnecessary", STEM_ROUNDING_MODE_UNNECESSARY, status);
@@ -217,6 +220,12 @@
return UNUM_ROUND_UP;
case STEM_ROUNDING_MODE_HALF_EVEN:
return UNUM_ROUND_HALFEVEN;
+ case STEM_ROUNDING_MODE_HALF_ODD:
+ return UNUM_ROUND_HALF_ODD;
+ case STEM_ROUNDING_MODE_HALF_CEILING:
+ return UNUM_ROUND_HALF_CEILING;
+ case STEM_ROUNDING_MODE_HALF_FLOOR:
+ return UNUM_ROUND_HALF_FLOOR;
case STEM_ROUNDING_MODE_HALF_DOWN:
return UNUM_ROUND_HALFDOWN;
case STEM_ROUNDING_MODE_HALF_UP:
@@ -320,6 +329,15 @@
case UNUM_ROUND_HALFEVEN:
sb.append(u"rounding-mode-half-even", -1);
break;
+ case UNUM_ROUND_HALF_ODD:
+ sb.append(u"rounding-mode-half-odd", -1);
+ break;
+ case UNUM_ROUND_HALF_CEILING:
+ sb.append(u"rounding-mode-half-ceiling", -1);
+ break;
+ case UNUM_ROUND_HALF_FLOOR:
+ sb.append(u"rounding-mode-half-floor", -1);
+ break;
case UNUM_ROUND_HALFDOWN:
sb.append(u"rounding-mode-half-down", -1);
break;
@@ -672,6 +690,9 @@
case STEM_ROUNDING_MODE_DOWN:
case STEM_ROUNDING_MODE_UP:
case STEM_ROUNDING_MODE_HALF_EVEN:
+ case STEM_ROUNDING_MODE_HALF_ODD:
+ case STEM_ROUNDING_MODE_HALF_CEILING:
+ case STEM_ROUNDING_MODE_HALF_FLOOR:
case STEM_ROUNDING_MODE_HALF_DOWN:
case STEM_ROUNDING_MODE_HALF_UP:
case STEM_ROUNDING_MODE_UNNECESSARY:
diff --git a/icu4c/source/i18n/number_skeletons.h b/icu4c/source/i18n/number_skeletons.h
index 5c1055b..cb3cdf3 100644
--- a/icu4c/source/i18n/number_skeletons.h
+++ b/icu4c/source/i18n/number_skeletons.h
@@ -85,6 +85,9 @@
STEM_ROUNDING_MODE_DOWN,
STEM_ROUNDING_MODE_UP,
STEM_ROUNDING_MODE_HALF_EVEN,
+ STEM_ROUNDING_MODE_HALF_ODD,
+ STEM_ROUNDING_MODE_HALF_CEILING,
+ STEM_ROUNDING_MODE_HALF_FLOOR,
STEM_ROUNDING_MODE_HALF_DOWN,
STEM_ROUNDING_MODE_HALF_UP,
STEM_ROUNDING_MODE_UNNECESSARY,
diff --git a/icu4c/source/i18n/unicode/unum.h b/icu4c/source/i18n/unicode/unum.h
index 705e6fe..6bcb7f6 100644
--- a/icu4c/source/i18n/unicode/unum.h
+++ b/icu4c/source/i18n/unicode/unum.h
@@ -302,7 +302,24 @@
* ROUND_UNNECESSARY reports an error if formatted result is not exact.
* @stable ICU 4.8
*/
- UNUM_ROUND_UNNECESSARY
+ UNUM_ROUND_UNNECESSARY,
+#ifndef U_HIDE_DRAFT_API
+ /**
+ * Rounds ties toward the odd number.
+ * @draft ICU 69
+ */
+ UNUM_ROUND_HALF_ODD,
+ /**
+ * Rounds ties toward +∞.
+ * @draft ICU 69
+ */
+ UNUM_ROUND_HALF_CEILING,
+ /**
+ * Rounds ties toward -∞.
+ * @draft ICU 69
+ */
+ UNUM_ROUND_HALF_FLOOR,
+#endif
} UNumberFormatRoundingMode;
/** The possible number format pad positions.
diff --git a/icu4c/source/test/cintltst/cnmdptst.c b/icu4c/source/test/cintltst/cnmdptst.c
index c252259..de5e710 100644
--- a/icu4c/source/test/cintltst/cnmdptst.c
+++ b/icu4c/source/test/cintltst/cnmdptst.c
@@ -968,7 +968,12 @@
roundingTest2(nnf, -0.125, UNUM_ROUND_FLOOR, "-0.13");
roundingTest2(nnf, -0.125, UNUM_ROUND_DOWN, "-0.12");
roundingTest2(nnf, -0.125, UNUM_ROUND_UP, "-0.13");
- roundingTest2(nnf, 0.125, UNUM_FOUND_HALFEVEN, "0.12");
+ roundingTest2(nnf, 0.125, UNUM_ROUND_HALFEVEN, "0.12");
+ roundingTest2(nnf, 0.135, UNUM_ROUND_HALF_ODD, "0.13");
+ roundingTest2(nnf, 0.135, UNUM_ROUND_HALF_CEILING, "0.14");
+ roundingTest2(nnf, -0.135, UNUM_ROUND_HALF_CEILING, "-0.13");
+ roundingTest2(nnf, 0.135, UNUM_ROUND_HALF_FLOOR, "0.13");
+ roundingTest2(nnf, -0.135, UNUM_ROUND_HALF_FLOOR, "-0.14");
roundingTest2(nnf, 0.135, UNUM_ROUND_HALFDOWN, "0.13");
roundingTest2(nnf, 0.125, UNUM_ROUND_HALFUP, "0.13");
roundingTest2(nnf, 0.135, UNUM_FOUND_HALFEVEN, "0.14");
@@ -984,6 +989,9 @@
roundingTest2(nnf, 1.0000001, UNUM_ROUND_DOWN, "1");
roundingTest2(nnf, 1.0000001, UNUM_ROUND_UP, "1.01");
roundingTest2(nnf, 1.0000001, UNUM_FOUND_HALFEVEN, "1");
+ roundingTest2(nnf, 1.0000001, UNUM_ROUND_HALF_ODD, "1");
+ roundingTest2(nnf, 1.0000001, UNUM_ROUND_HALF_CEILING, "1");
+ roundingTest2(nnf, 1.0000001, UNUM_ROUND_HALF_FLOOR, "1");
roundingTest2(nnf, 1.0000001, UNUM_ROUND_HALFDOWN, "1");
roundingTest2(nnf, 1.0000001, UNUM_ROUND_HALFUP, "1");
@@ -992,6 +1000,9 @@
roundingTest2(nnf, -1.0000001, UNUM_ROUND_DOWN, "-1");
roundingTest2(nnf, -1.0000001, UNUM_ROUND_UP, "-1.01");
roundingTest2(nnf, -1.0000001, UNUM_FOUND_HALFEVEN, "-1");
+ roundingTest2(nnf, -1.0000001, UNUM_ROUND_HALF_ODD, "-1");
+ roundingTest2(nnf, -1.0000001, UNUM_ROUND_HALF_CEILING, "-1");
+ roundingTest2(nnf, -1.0000001, UNUM_ROUND_HALF_FLOOR, "-1");
roundingTest2(nnf, -1.0000001, UNUM_ROUND_HALFDOWN, "-1");
roundingTest2(nnf, -1.0000001, UNUM_ROUND_HALFUP, "-1");
diff --git a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp
index 9339396..74b97a9 100644
--- a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp
+++ b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp
@@ -413,6 +413,9 @@
{1.024, -2, UNUM_ROUND_HALFEVEN, u"1"},
{1.025, -2, UNUM_ROUND_HALFEVEN, u"1"},
{1.025, -2, UNUM_ROUND_HALFDOWN, u"1"},
+ {1.025, -2, UNUM_ROUND_HALF_ODD, u"1.05"},
+ {1.025, -2, UNUM_ROUND_HALF_CEILING, u"1.05"},
+ {1.025, -2, UNUM_ROUND_HALF_FLOOR, u"1"},
{1.025, -2, UNUM_ROUND_HALFUP, u"1.05"},
{1.026, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
{1.030, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
@@ -422,6 +425,9 @@
{1.070, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
{1.074, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
{1.075, -2, UNUM_ROUND_HALFDOWN, u"1.05"},
+ {1.075, -2, UNUM_ROUND_HALF_ODD, u"1.05"},
+ {1.075, -2, UNUM_ROUND_HALF_CEILING, u"1.1"},
+ {1.075, -2, UNUM_ROUND_HALF_FLOOR, u"1.05"},
{1.075, -2, UNUM_ROUND_HALFUP, u"1.1"},
{1.075, -2, UNUM_ROUND_HALFEVEN, u"1.1"},
{1.076, -2, UNUM_ROUND_HALFEVEN, u"1.1"},
@@ -432,6 +438,9 @@
{2.25, -1, UNUM_ROUND_HALFEVEN, u"2"},
{2.25, -1, UNUM_ROUND_HALFUP, u"2.5"},
{2.75, -1, UNUM_ROUND_HALFDOWN, u"2.5"},
+ {2.75, -1, UNUM_ROUND_HALF_ODD, u"2.5"},
+ {2.75, -1, UNUM_ROUND_HALF_CEILING, u"3"},
+ {2.75, -1, UNUM_ROUND_HALF_FLOOR, u"2.5"},
{2.75, -1, UNUM_ROUND_HALFEVEN, u"3"},
{3.00, -1, UNUM_ROUND_CEILING, u"3"},
{3.25, -1, UNUM_ROUND_CEILING, u"3.5"},