ICU-22692 Change SimpleNumber truncateAt to setMaximumIntegerDigits

Also promotes the remaining draft SimpleNumber functions to stable.

See #2892
This commit is contained in:
Shane F. Carr 2024-03-26 17:59:25 +00:00
parent 57fc3094f9
commit 8a1df5a7f4
14 changed files with 234 additions and 49 deletions

View file

@ -309,12 +309,17 @@ usnum_setMinimumFractionDigits(USimpleNumber* unumber, int32_t minimumFractionDi
}
U_CAPI void U_EXPORT2
usnum_truncateStart(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec) {
usnum_setMaximumIntegerDigits(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec) {
auto* number = USimpleNumberData::validate(unumber, *ec);
if (U_FAILURE(*ec)) {
return;
}
number->fNumber.truncateStart(maximumIntegerDigits, *ec);
number->fNumber.setMaximumIntegerDigits(maximumIntegerDigits, *ec);
}
U_CAPI void U_EXPORT2
usnum_truncateStart(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec) {
usnum_setMaximumIntegerDigits(unumber, maximumIntegerDigits, ec);
}
U_CAPI void U_EXPORT2

View file

@ -131,18 +131,24 @@ void DecimalQuantity::clear() {
setBcdToZero(); // sets scale, precision, hasDouble, origDouble, origDelta, and BCD data
}
void DecimalQuantity::setMinInteger(int32_t minInt) {
void DecimalQuantity::decreaseMinIntegerTo(int32_t minInt) {
// Validation should happen outside of DecimalQuantity, e.g., in the Precision class.
U_ASSERT(minInt >= 0);
if (lReqPos > minInt) {
lReqPos = minInt;
}
}
void DecimalQuantity::increaseMinIntegerTo(int32_t minInt) {
// Validation should happen outside of DecimalQuantity, e.g., in the Precision class.
U_ASSERT(minInt >= 0);
// Special behavior: do not set minInt to be less than what is already set.
// This is so significant digits rounding can set the integer length.
if (minInt < lReqPos) {
minInt = lReqPos;
if (lReqPos < minInt) {
lReqPos = minInt;
}
// Save values into internal state
lReqPos = minInt;
}
void DecimalQuantity::setMinFraction(int32_t minFrac) {

View file

@ -52,13 +52,21 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
/** Move assignment */
DecimalQuantity &operator=(DecimalQuantity&& src) noexcept;
/**
* If the minimum integer digits are greater than `minInt`,
* sets it to `minInt`.
*
* @param minInt The minimum number of integer digits.
*/
void decreaseMinIntegerTo(int32_t minInt);
/**
* Sets the minimum integer digits that this {@link DecimalQuantity} should generate.
* This method does not perform rounding.
*
* @param minInt The minimum number of integer digits.
*/
void setMinInteger(int32_t minInt);
void increaseMinIntegerTo(int32_t minInt);
/**
* Sets the minimum fraction digits that this {@link DecimalQuantity} should generate.

View file

@ -46,14 +46,14 @@ void IntegerWidth::apply(impl::DecimalQuantity& quantity, UErrorCode& status) co
if (fHasError) {
status = U_ILLEGAL_ARGUMENT_ERROR;
} else if (fUnion.minMaxInt.fMaxInt == -1) {
quantity.setMinInteger(fUnion.minMaxInt.fMinInt);
quantity.increaseMinIntegerTo(fUnion.minMaxInt.fMinInt);
} else {
// Enforce the backwards-compatibility feature "FormatFailIfMoreThanMaxDigits"
if (fUnion.minMaxInt.fFormatFailIfMoreThanMaxDigits &&
fUnion.minMaxInt.fMaxInt < quantity.getMagnitude()) {
status = U_ILLEGAL_ARGUMENT_ERROR;
}
quantity.setMinInteger(fUnion.minMaxInt.fMinInt);
quantity.increaseMinIntegerTo(fUnion.minMaxInt.fMinInt);
quantity.applyMaxInteger(fUnion.minMaxInt.fMaxInt);
}
}

View file

@ -768,7 +768,7 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
incrementQuantity.roundToInfinity();
digitsStringScale = incrementQuantity.getLowerDisplayMagnitude();
incrementQuantity.adjustMagnitude(-digitsStringScale);
incrementQuantity.setMinInteger(minInt - digitsStringScale);
incrementQuantity.increaseMinIntegerTo(minInt - digitsStringScale);
UnicodeString str = incrementQuantity.toPlainString();
if (str.charAt(0) == u'-') {
// TODO: Unsupported operation exception or fail silently?

View file

@ -437,7 +437,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig));
// Make sure that digits are displayed on zero.
if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) {
value.setMinInteger(1);
value.increaseMinIntegerTo(1);
}
break;

View file

@ -81,7 +81,8 @@ void SimpleNumber::setMinimumIntegerDigits(uint32_t position, UErrorCode& status
status = U_INVALID_STATE_ERROR;
return;
}
fData->quantity.setMinInteger(position);
fData->quantity.decreaseMinIntegerTo(position);
fData->quantity.increaseMinIntegerTo(position);
}
void SimpleNumber::setMinimumFractionDigits(uint32_t position, UErrorCode& status) {
@ -95,7 +96,7 @@ void SimpleNumber::setMinimumFractionDigits(uint32_t position, UErrorCode& statu
fData->quantity.setMinFraction(position);
}
void SimpleNumber::truncateStart(uint32_t position, UErrorCode& status) {
void SimpleNumber::setMaximumIntegerDigits(uint32_t position, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
@ -103,9 +104,14 @@ void SimpleNumber::truncateStart(uint32_t position, UErrorCode& status) {
status = U_INVALID_STATE_ERROR;
return;
}
fData->quantity.decreaseMinIntegerTo(position);
fData->quantity.applyMaxInteger(position);
}
void SimpleNumber::truncateStart(uint32_t position, UErrorCode& status) {
setMaximumIntegerDigits(position, status);
}
void SimpleNumber::setSign(USimpleNumberSign sign, UErrorCode& status) {
if (U_FAILURE(status)) {
return;

View file

@ -2164,7 +2164,7 @@ SimpleDateFormat::zeroPaddingNumber(
data.quantity.setToLong(value);
number::SimpleNumber number(&data, localStatus);
number.setMinimumIntegerDigits(minDigits, localStatus);
number.truncateStart(maxDigits, localStatus);
number.setMaximumIntegerDigits(maxDigits, localStatus);
number::FormattedNumber result = fSimpleNumberFormatter->format(std::move(number), localStatus);
if (U_FAILURE(localStatus)) {

View file

@ -68,44 +68,49 @@ class U_I18N_API SimpleNumber : public UMemory {
*/
void multiplyByPowerOfTen(int32_t power, UErrorCode& status);
#ifndef U_HIDE_DRAFT_API
/**
* Rounds the value currently stored in the SimpleNumber to the given power of 10.
* Rounds the value currently stored in the SimpleNumber to the given power of 10,
* which can be before or after the decimal separator.
*
* This function immediately mutates the inner value.
* This function does not change minimum integer digits.
*
* @draft ICU 73
* @stable ICU 73
*/
void roundTo(int32_t power, UNumberFormatRoundingMode roundingMode, UErrorCode& status);
#ifndef U_HIDE_DRAFT_API
/**
* Truncates the most significant digits to the given maximum number of integer digits.
* Sets the number of integer digits to the given amount, truncating if necessary.
*
* This function immediately mutates the inner value.
* @draft ICU 75
*/
void setMaximumIntegerDigits(uint32_t maximumIntegerDigits, UErrorCode& status);
#endif // U_HIDE_DRAFT_API
#ifndef U_HIDE_DEPRECATED_API
/**
* Alias for setMaximumIntegerDigits.
* Will be removed after ICU 75.
*
* @draft ICU 73
* @deprecated ICU 75
*/
void truncateStart(uint32_t maximumIntegerDigits, UErrorCode& status);
#endif // U_HIDE_DEPRECATED_API
/**
* Pads the beginning of the number with zeros up to the given minimum number of integer digits.
*
* This setting is applied upon formatting the number.
*
* @draft ICU 73
* @stable ICU 73
*/
void setMinimumIntegerDigits(uint32_t minimumIntegerDigits, UErrorCode& status);
/**
* Pads the end of the number with zeros up to the given minimum number of fraction digits.
*
* This setting is applied upon formatting the number.
*
* @draft ICU 73
* @stable ICU 73
*/
void setMinimumFractionDigits(uint32_t minimumFractionDigits, UErrorCode& status);
#endif // U_HIDE_DRAFT_API
/**
* Sets the sign of the number: an explicit plus sign, explicit minus sign, or no sign.
*

View file

@ -126,13 +126,13 @@ U_CAPI void U_EXPORT2
usnum_multiplyByPowerOfTen(USimpleNumber* unumber, int32_t power, UErrorCode* ec);
#ifndef U_HIDE_DRAFT_API
/**
* Rounds the value currently stored in the USimpleNumber to the given power of 10.
* Rounds the value currently stored in the USimpleNumber to the given power of 10,
* which can be before or after the decimal separator.
*
* This function immediately mutates the inner value.
* This function does not change minimum integer digits.
*
* @draft ICU 73
* @stable ICU 73
*/
U_CAPI void U_EXPORT2
usnum_roundTo(USimpleNumber* unumber, int32_t power, UNumberFormatRoundingMode roundingMode, UErrorCode* ec);
@ -141,9 +141,7 @@ usnum_roundTo(USimpleNumber* unumber, int32_t power, UNumberFormatRoundingMode r
/**
* Pads the beginning of the number with zeros up to the given minimum number of integer digits.
*
* This setting is applied upon formatting the number.
*
* @draft ICU 73
* @stable ICU 73
*/
U_CAPI void U_EXPORT2
usnum_setMinimumIntegerDigits(USimpleNumber* unumber, int32_t minimumIntegerDigits, UErrorCode* ec);
@ -152,25 +150,34 @@ usnum_setMinimumIntegerDigits(USimpleNumber* unumber, int32_t minimumIntegerDigi
/**
* Pads the end of the number with zeros up to the given minimum number of fraction digits.
*
* This setting is applied upon formatting the number.
*
* @draft ICU 73
* @stable ICU 73
*/
U_CAPI void U_EXPORT2
usnum_setMinimumFractionDigits(USimpleNumber* unumber, int32_t minimumFractionDigits, UErrorCode* ec);
#ifndef U_HIDE_DRAFT_API
/**
* Truncates digits from the beginning of the number to the given maximum number of integer digits.
* Sets the number of integer digits to the given amount, truncating if necessary.
*
* This function immediately mutates the inner value.
* @draft ICU 75
*/
U_CAPI void U_EXPORT2
usnum_setMaximumIntegerDigits(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec);
#endif // U_HIDE_DRAFT_API
#ifndef U_HIDE_DEPRECATED_API
/**
* Alias for setMaximumIntegerDigits.
* Will be removed after ICU 75.
*
* @draft ICU 73
* @deprecated ICU 75
*/
U_CAPI void U_EXPORT2
usnum_truncateStart(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec);
#endif // U_HIDE_DEPRECATED_API
#endif // U_HIDE_DRAFT_API
/**
* Sets the sign of the number: an explicit plus sign, explicit minus sign, or no sign.

View file

@ -255,9 +255,9 @@ static void TestSimpleNumberFormatterFull(void) {
usnum_setToInt64(unumber, 98765, &ec);
usnum_multiplyByPowerOfTen(unumber, -2, &ec);
usnum_roundTo(unumber, -1, UNUM_ROUND_HALFDOWN, &ec);
usnum_setMaximumIntegerDigits(unumber, 1, &ec);
usnum_setMinimumIntegerDigits(unumber, 4, &ec);
usnum_setMinimumFractionDigits(unumber, 3, &ec);
usnum_truncateStart(unumber, 1, &ec);
usnum_setSign(unumber, UNUM_SIMPLE_NUMBER_PLUS_SIGN, &ec);
usnumf_format(uformatter, unumber, uresult, &ec);

View file

@ -373,6 +373,7 @@ class SimpleNumberFormatterTest : public IntlTestWithFieldPosition {
public:
void testBasic();
void testWithOptions();
void testDigits();
void testSymbols();
void testSign();
void testCopyMove();

View file

@ -92,7 +92,7 @@ void DecimalQuantityTest::testDecimalQuantityBehaviorStandalone() {
fq.setToLong(90909090909000L);
assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 90909090909E3>");
fq.setMinInteger(2);
fq.increaseMinIntegerTo(2);
fq.applyMaxInteger(5);
assertToStringAndHealth(fq, u"<DecimalQuantity 2:0 long 9E3>");
fq.setMinFraction(3);
@ -424,7 +424,7 @@ void DecimalQuantityTest::testMaxDigits() {
DecimalQuantity dq;
dq.setToDouble(876.543);
dq.roundToInfinity();
dq.setMinInteger(0);
dq.increaseMinIntegerTo(0);
dq.applyMaxInteger(2);
dq.setMinFraction(0);
dq.roundToMagnitude(-2, UNUM_ROUND_FLOOR, status);

View file

@ -18,6 +18,7 @@ void SimpleNumberFormatterTest::runIndexedTest(int32_t index, UBool exec, const
TESTCASE_AUTO_BEGIN;
TESTCASE_AUTO(testBasic);
TESTCASE_AUTO(testWithOptions);
TESTCASE_AUTO(testDigits);
TESTCASE_AUTO(testSymbols);
TESTCASE_AUTO(testSign);
TESTCASE_AUTO(testCopyMove);
@ -51,11 +52,11 @@ void SimpleNumberFormatterTest::testWithOptions() {
IcuTestErrorCode status(*this, "testWithOptions");
SimpleNumber num = SimpleNumber::forInt64(1250000, status);
num.setMaximumIntegerDigits(6, status);
num.setMinimumIntegerDigits(6, status);
num.setMinimumFractionDigits(2, status);
num.multiplyByPowerOfTen(-2, status);
num.roundTo(3, UNUM_ROUND_HALFUP, status);
num.truncateStart(4, status);
SimpleNumberFormatter snf = SimpleNumberFormatter::forLocale("bn", status);
FormattedNumber result = snf.format(std::move(num), status);
@ -76,6 +77,152 @@ void SimpleNumberFormatterTest::testWithOptions() {
UPRV_LENGTHOF(expectedFieldPositions));
}
void SimpleNumberFormatterTest::testDigits() {
IcuTestErrorCode status(*this, "testDigits");
SimpleNumberFormatter snf = SimpleNumberFormatter::forLocaleAndGroupingStrategy(
"en-US",
UNUM_GROUPING_ON_ALIGNED,
status
);
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
auto result = snf.format(std::move(num), status);
assertEquals("no digit options",
u"91,827.3645",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMaximumIntegerDigits(4, status);
auto result = snf.format(std::move(num), status);
assertEquals("setMaximumIntegerDigits",
u"1,827.3645",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMaximumIntegerDigits(8, status);
auto result = snf.format(std::move(num), status);
assertEquals("bigger setMaximumIntegerDigits",
u"91,827.3645",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMinimumIntegerDigits(6, status);
auto result = snf.format(std::move(num), status);
assertEquals("setMinimumIntegerDigits",
u"091,827.3645",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMinimumIntegerDigits(8, status);
num.setMinimumIntegerDigits(6, status);
auto result = snf.format(std::move(num), status);
assertEquals("double setMinimumIntegerDigits",
u"091,827.3645",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMaximumIntegerDigits(4, status);
num.setMinimumIntegerDigits(6, status);
auto result = snf.format(std::move(num), status);
assertEquals("setMaximumIntegerDigits setMinimumIntegerDigits",
u"001,827.3645",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMinimumIntegerDigits(6, status);
num.setMaximumIntegerDigits(4, status);
auto result = snf.format(std::move(num), status);
assertEquals("setMinimumIntegerDigits setMaximumIntegerDigits",
u"1,827.3645",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMaximumIntegerDigits(8, status);
num.setMinimumIntegerDigits(6, status);
auto result = snf.format(std::move(num), status);
assertEquals("bigger setMaximumIntegerDigits setMinimumIntegerDigits",
u"091,827.3645",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.roundTo(-1, UNUM_ROUND_HALFEVEN, status);
auto result = snf.format(std::move(num), status);
assertEquals("roundTo",
u"91,827.4",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMinimumFractionDigits(5, status);
auto result = snf.format(std::move(num), status);
assertEquals("setMinimumFractionDigits",
u"91,827.36450",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMinimumFractionDigits(6, status);
num.setMinimumFractionDigits(5, status);
auto result = snf.format(std::move(num), status);
assertEquals("double setMinimumFractionDigits",
u"91,827.36450",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.roundTo(-1, UNUM_ROUND_HALFEVEN, status);
num.setMinimumFractionDigits(5, status);
auto result = snf.format(std::move(num), status);
assertEquals("roundTo setMinimumFractionDigits",
u"91,827.40000",
result.toTempString(status));
}
{
SimpleNumber num = SimpleNumber::forInt64(918273645, status);
num.multiplyByPowerOfTen(-4, status);
num.setMinimumFractionDigits(5, status);
num.roundTo(-1, UNUM_ROUND_HALFEVEN, status);
auto result = snf.format(std::move(num), status);
assertEquals("setMinimumFractionDigits roundTo",
u"91,827.40000",
result.toTempString(status));
}
}
void SimpleNumberFormatterTest::testSymbols() {
IcuTestErrorCode status(*this, "testSymbols");
@ -199,7 +346,7 @@ void SimpleNumberFormatterTest::testCAPI() {
usnum_setToInt64(unumber.getAlias(), 2335, status);
usnum_multiplyByPowerOfTen(unumber.getAlias(), -2, status);
usnum_roundTo(unumber.getAlias(), -1, UNUM_ROUND_HALFEVEN, status);
usnum_truncateStart(unumber.getAlias(), 1, status);
usnum_setMaximumIntegerDigits(unumber.getAlias(), 1, status);
usnum_setMinimumFractionDigits(unumber.getAlias(), 3, status);
usnum_setMinimumIntegerDigits(unumber.getAlias(), 3, status);
usnumf_format(uformatter.getAlias(), unumber.getAlias(), uresult.getAlias(), status);