ICU-21493 Add more rounding modes in ICU4C

See #1575
This commit is contained in:
Shane F. Carr 2021-02-25 02:49:16 +00:00 committed by Shane F. Carr
parent d7db6c1f86
commit e8dfea9bb6
7 changed files with 162 additions and 46 deletions

View file

@ -30,50 +30,52 @@ definitions, but are equivalent to the same modes used in Java's JDK.
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 @@ default mode specified for IEEE 754 floating point operations.
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 @@ removed.
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

View file

@ -104,6 +104,45 @@ getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode
}
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;
}

View file

@ -68,6 +68,9 @@ void U_CALLCONV initNumberSkeletons(UErrorCode& status) {
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 @@ UNumberFormatRoundingMode stem_to_object::roundingMode(skeleton::StemEnum stem)
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 @@ void enum_to_stem_string::roundingMode(UNumberFormatRoundingMode value, UnicodeS
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 @@ skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, Se
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:

View file

@ -85,6 +85,9 @@ enum StemEnum {
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,

View file

@ -302,7 +302,24 @@ typedef enum UNumberFormatRoundingMode {
* 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.

View file

@ -968,7 +968,12 @@ static void TestRounding5350(void)
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 @@ static void TestRounding5350(void)
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 @@ static void TestRounding5350(void)
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");

View file

@ -413,6 +413,9 @@ void DecimalQuantityTest::testNickelRounding() {
{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 @@ void DecimalQuantityTest::testNickelRounding() {
{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 @@ void DecimalQuantityTest::testNickelRounding() {
{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"},