]> granicus.if.org Git - icu/commitdiff
ICU-13701 Refactoring DecimalQuantity: removing lOptPos/rOptPos.
authorShane Carr <shane@unicode.org>
Thu, 25 Oct 2018 04:25:39 +0000 (21:25 -0700)
committerShane F. Carr <shane@unicode.org>
Thu, 8 Nov 2018 09:43:31 +0000 (02:43 -0700)
Combined ICU4C and ICU4J.

14 files changed:
icu4c/source/i18n/number_decimalquantity.cpp
icu4c/source/i18n/number_decimalquantity.h
icu4c/source/i18n/number_integerwidth.cpp
icu4c/source/i18n/number_rounding.cpp
icu4c/source/test/intltest/numbertest_decimalquantity.cpp
icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java
icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
icu4j/main/classes/core/src/com/ibm/icu/number/Precision.java
icu4j/main/tests/core/src/com/ibm/icu/dev/impl/number/DecimalQuantity_64BitBCD.java
icu4j/main/tests/core/src/com/ibm/icu/dev/impl/number/DecimalQuantity_ByteArrayBCD.java
icu4j/main/tests/core/src/com/ibm/icu/dev/impl/number/DecimalQuantity_SimpleStorage.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java

index d5dd7ae694c760d78fa66a0d8b7e51bad6b312cf..5a1fc3312063fec8ddc022a40f2472a207cab479 100644 (file)
@@ -112,10 +112,8 @@ DecimalQuantity& DecimalQuantity::operator=(DecimalQuantity&& src) U_NOEXCEPT {
 
 void DecimalQuantity::copyFieldsFrom(const DecimalQuantity& other) {
     bogus = other.bogus;
-    lOptPos = other.lOptPos;
     lReqPos = other.lReqPos;
     rReqPos = other.rReqPos;
-    rOptPos = other.rOptPos;
     scale = other.scale;
     precision = other.precision;
     flags = other.flags;
@@ -125,18 +123,15 @@ void DecimalQuantity::copyFieldsFrom(const DecimalQuantity& other) {
 }
 
 void DecimalQuantity::clear() {
-    lOptPos = INT32_MAX;
     lReqPos = 0;
     rReqPos = 0;
-    rOptPos = INT32_MIN;
     flags = 0;
     setBcdToZero(); // sets scale, precision, hasDouble, origDouble, origDelta, and BCD data
 }
 
-void DecimalQuantity::setIntegerLength(int32_t minInt, int32_t maxInt) {
+void DecimalQuantity::setMinInteger(int32_t minInt) {
     // Validation should happen outside of DecimalQuantity, e.g., in the Precision class.
     U_ASSERT(minInt >= 0);
-    U_ASSERT(maxInt >= minInt);
 
     // 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.
@@ -145,28 +140,37 @@ void DecimalQuantity::setIntegerLength(int32_t minInt, int32_t maxInt) {
     }
 
     // Save values into internal state
-    // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
-    lOptPos = maxInt;
     lReqPos = minInt;
 }
 
-void DecimalQuantity::setFractionLength(int32_t minFrac, int32_t maxFrac) {
+void DecimalQuantity::setMinFraction(int32_t minFrac) {
     // Validation should happen outside of DecimalQuantity, e.g., in the Precision class.
     U_ASSERT(minFrac >= 0);
-    U_ASSERT(maxFrac >= minFrac);
 
     // Save values into internal state
     // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
     rReqPos = -minFrac;
-    rOptPos = -maxFrac;
+}
+
+void DecimalQuantity::applyMaxInteger(int32_t maxInt) {
+    // Validation should happen outside of DecimalQuantity, e.g., in the Precision class.
+    U_ASSERT(maxInt >= 0);
+
+    if (precision == 0) {
+        return;
+    }
+
+    int32_t magnitude = getMagnitude();
+    if (maxInt <= magnitude) {
+        popFromLeft(magnitude - maxInt + 1);
+        compact();
+    }
 }
 
 uint64_t DecimalQuantity::getPositionFingerprint() const {
     uint64_t fingerprint = 0;
-    fingerprint ^= lOptPos;
     fingerprint ^= (lReqPos << 16);
     fingerprint ^= (static_cast<uint64_t>(rReqPos) << 32);
-    fingerprint ^= (static_cast<uint64_t>(rOptPos) << 48);
     return fingerprint;
 }
 
@@ -280,7 +284,7 @@ int32_t DecimalQuantity::getUpperDisplayMagnitude() const {
     U_ASSERT(!isApproximate);
 
     int32_t magnitude = scale + precision;
-    int32_t result = (lReqPos > magnitude) ? lReqPos : (lOptPos < magnitude) ? lOptPos : magnitude;
+    int32_t result = (lReqPos > magnitude) ? lReqPos : magnitude;
     return result - 1;
 }
 
@@ -290,7 +294,7 @@ int32_t DecimalQuantity::getLowerDisplayMagnitude() const {
     U_ASSERT(!isApproximate);
 
     int32_t magnitude = scale;
-    int32_t result = (rReqPos < magnitude) ? rReqPos : (rOptPos > magnitude) ? rOptPos : magnitude;
+    int32_t result = (rReqPos < magnitude) ? rReqPos : magnitude;
     return result;
 }
 
@@ -511,7 +515,7 @@ int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const {
     // if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ }
     // Fallback behavior upon truncateIfOverflow is to truncate at 17 digits.
     uint64_t result = 0L;
-    int32_t upperMagnitude = std::min(scale + precision, lOptPos) - 1;
+    int32_t upperMagnitude = scale + precision - 1;
     if (truncateIfOverflow) {
         upperMagnitude = std::min(upperMagnitude, 17);
     }
@@ -527,7 +531,7 @@ int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const {
 uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const {
     uint64_t result = 0L;
     int32_t magnitude = -1;
-    int32_t lowerMagnitude = std::max(scale, rOptPos);
+    int32_t lowerMagnitude = scale;
     if (includeTrailingZeros) {
         lowerMagnitude = std::min(lowerMagnitude, rReqPos);
     }
@@ -884,10 +888,8 @@ UnicodeString DecimalQuantity::toScientificString() const {
         result.append(u"0E+0", -1);
         return result;
     }
-    // NOTE: It is not safe to add to lOptPos (aka maxInt) or subtract from
-    // rOptPos (aka -maxFrac) due to overflow.
-    int32_t upperPos = std::min(precision + scale, lOptPos) - scale - 1;
-    int32_t lowerPos = std::max(scale, rOptPos) - scale;
+    int32_t upperPos = precision - 1;
+    int32_t lowerPos = 0;
     int32_t p = upperPos;
     result.append(u'0' + getDigitPos(p));
     if ((--p) >= lowerPos) {
@@ -985,6 +987,18 @@ void DecimalQuantity::shiftRight(int32_t numDigits) {
     precision -= numDigits;
 }
 
+void DecimalQuantity::popFromLeft(int32_t numDigits) {
+    if (usingBytes) {
+        int i = precision - 1;
+        for (; i >= precision - numDigits; i--) {
+            fBCD.bcdBytes.ptr[i] = 0;
+        }
+    } else {
+        fBCD.bcdLong &= (static_cast<uint64_t>(1) << ((precision - numDigits) * 4)) - 1;
+    }
+    precision -= numDigits;
+}
+
 void DecimalQuantity::setBcdToZero() {
     if (usingBytes) {
         uprv_free(fBCD.bcdBytes.ptr);
@@ -1239,10 +1253,8 @@ bool DecimalQuantity::operator==(const DecimalQuantity& other) const {
             scale == other.scale
             && precision == other.precision
             && flags == other.flags
-            && lOptPos == other.lOptPos
             && lReqPos == other.lReqPos
             && rReqPos == other.rReqPos
-            && rOptPos == other.rOptPos
             && isApproximate == other.isApproximate;
     if (!basicEquals) {
         return false;
@@ -1272,11 +1284,9 @@ UnicodeString DecimalQuantity::toString() const {
     snprintf(
             buffer8,
             sizeof(buffer8),
-            "<DecimalQuantity %d:%d:%d:%d %s %s%s%s%d>",
-            (lOptPos > 999 ? 999 : lOptPos),
+            "<DecimalQuantity %d:%d %s %s%s%s%d>",
             lReqPos,
             rReqPos,
-            (rOptPos < -999 ? -999 : rOptPos),
             (usingBytes ? "bytes" : "long"),
             (isNegative() ? "-" : ""),
             (precision == 0 ? "0" : digits.getAlias()),
index 2bef2514bceaa65b79e651a1901b709e50ce71f5..af00917e2b90faed0feccaf6af9f54887a2dabc2 100644 (file)
@@ -53,22 +53,28 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
     DecimalQuantity &operator=(DecimalQuantity&& src) U_NOEXCEPT;
 
     /**
-     * Sets the minimum and maximum integer digits that this {@link DecimalQuantity} should generate.
+     * 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.
-     * @param maxInt The maximum number of integer digits.
      */
-    void setIntegerLength(int32_t minInt, int32_t maxInt);
+    void setMinInteger(int32_t minInt);
 
     /**
-     * Sets the minimum and maximum fraction digits that this {@link DecimalQuantity} should generate.
+     * Sets the minimum fraction digits that this {@link DecimalQuantity} should generate.
      * This method does not perform rounding.
      *
      * @param minFrac The minimum number of fraction digits.
-     * @param maxFrac The maximum number of fraction digits.
      */
-    void setFractionLength(int32_t minFrac, int32_t maxFrac);
+    void setMinFraction(int32_t minFrac);
+
+    /**
+     * Truncates digits from the upper magnitude of the number in order to satisfy the
+     * specified maximum number of integer digits.
+     *
+     * @param maxInt The maximum number of integer digits.
+     */
+    void applyMaxInteger(int32_t maxInt);
 
     /**
      * Rounds the number to a specified interval, such as 0.05.
@@ -336,36 +342,11 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
      */
     int32_t origDelta;
 
-    // Four positions: left optional '(', left required '[', right required ']', right optional ')'.
-    // These four positions determine which digits are displayed in the output string.  They do NOT
-    // affect rounding.  These positions are internal-only and can be specified only by the public
-    // endpoints like setFractionLength, setIntegerLength, and setSignificantDigits, among others.
-    //
-    //   * Digits between lReqPos and rReqPos are in the "required zone" and are always displayed.
-    //   * Digits between lOptPos and rOptPos but outside the required zone are in the "optional zone"
-    //     and are displayed unless they are trailing off the left or right edge of the number and
-    //     have a numerical value of zero.  In order to be "trailing", the digits need to be beyond
-    //     the decimal point in their respective directions.
-    //   * Digits outside of the "optional zone" are never displayed.
-    //
-    // See the table below for illustrative examples.
-    //
-    // +---------+---------+---------+---------+------------+------------------------+--------------+
-    // | lOptPos | lReqPos | rReqPos | rOptPos |   number   |        positions       | en-US string |
-    // +---------+---------+---------+---------+------------+------------------------+--------------+
-    // |    5    |    2    |   -1    |   -5    |   1234.567 |     ( 12[34.5]67  )    |   1,234.567  |
-    // |    3    |    2    |   -1    |   -5    |   1234.567 |      1(2[34.5]67  )    |     234.567  |
-    // |    3    |    2    |   -1    |   -2    |   1234.567 |      1(2[34.5]6)7      |     234.56   |
-    // |    6    |    4    |    2    |   -5    | 123456789. |  123(45[67]89.     )   | 456,789.     |
-    // |    6    |    4    |    2    |    1    | 123456789. |     123(45[67]8)9.     | 456,780.     |
-    // |   -1    |   -1    |   -3    |   -4    | 0.123456   |     0.1([23]4)56       |        .0234 |
-    // |    6    |    4    |   -2    |   -2    |     12.3   |     (  [  12.3 ])      |    0012.30   |
-    // +---------+---------+---------+---------+------------+------------------------+--------------+
-    //
-    int32_t lOptPos = INT32_MAX;
+    // Positions to keep track of leading and trailing zeros.
+    // lReqPos is the magnitude of the first required leading zero.
+    // rReqPos is the magnitude of the last required trailing zero.
     int32_t lReqPos = 0;
     int32_t rReqPos = 0;
-    int32_t rOptPos = INT32_MIN;
 
     /**
      * The BCD of the 16 digits of the number represented by this object. Every 4 bits of the long map
@@ -421,8 +402,22 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
      */
     void shiftLeft(int32_t numDigits);
 
+    /**
+     * Directly removes digits from the end of the BCD list.
+     * Updates the scale and precision.
+     *
+     * CAUTION: it is the caller's responsibility to call {@link #compact} after this method.
+     */
     void shiftRight(int32_t numDigits);
 
+    /**
+     * Directly removes digits from the front of the BCD list.
+     * Updates precision.
+     *
+     * CAUTION: it is the caller's responsibility to call {@link #compact} after this method.
+     */
+    void popFromLeft(int32_t numDigits);
+
     /**
      * Sets the internal representation to zero. Clears any values stored in scale, precision,
      * hasDouble, origDouble, origDelta, and BCD data.
index 6416b292982e56927ca79f299fa0fd8a7dfd8b5d..d62aef444dca96f9ac5b62186948e7a9cf297653 100644 (file)
@@ -43,14 +43,15 @@ void IntegerWidth::apply(impl::DecimalQuantity& quantity, UErrorCode& status) co
     if (fHasError) {
         status = U_ILLEGAL_ARGUMENT_ERROR;
     } else if (fUnion.minMaxInt.fMaxInt == -1) {
-        quantity.setIntegerLength(fUnion.minMaxInt.fMinInt, INT32_MAX);
+        quantity.setMinInteger(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.setIntegerLength(fUnion.minMaxInt.fMinInt, fUnion.minMaxInt.fMaxInt);
+        quantity.setMinInteger(fUnion.minMaxInt.fMinInt);
+        quantity.applyMaxInteger(fUnion.minMaxInt.fMaxInt);
     }
 }
 
index ae4b8849fbe95638e81fe72e29147b418f6cc317..44ea800ce676eadf6f8a35dd23928f4b1b5643a2 100644 (file)
@@ -348,9 +348,8 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
                     getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac),
                     fRoundingMode,
                     status);
-            value.setFractionLength(
-                    uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)),
-                    INT32_MAX);
+            value.setMinFraction(
+                    uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)));
             break;
 
         case Precision::RND_SIGNIFICANT:
@@ -358,12 +357,11 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
                     getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig),
                     fRoundingMode,
                     status);
-            value.setFractionLength(
-                    uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)),
-                    INT32_MAX);
+            value.setMinFraction(
+                    uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)));
             // Make sure that digits are displayed on zero.
             if (value.isZero() && fPrecision.fUnion.fracSig.fMinSig > 0) {
-                value.setIntegerLength(1, INT32_MAX);
+                value.setMinInteger(1);
             }
             break;
 
@@ -384,7 +382,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
                 roundingMag = uprv_min(roundingMag, candidate);
             }
             value.roundToMagnitude(roundingMag, fRoundingMode, status);
-            value.setFractionLength(uprv_max(0, -displayMag), INT32_MAX);
+            value.setMinFraction(uprv_max(0, -displayMag));
             break;
         }
 
@@ -394,7 +392,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
                     fRoundingMode,
                     fPrecision.fUnion.increment.fMaxFrac,
                     status);
-            value.setFractionLength(fPrecision.fUnion.increment.fMinFrac, INT32_MAX);
+            value.setMinFraction(fPrecision.fUnion.increment.fMinFrac);
             break;
 
         case Precision::RND_CURRENCY:
@@ -408,7 +406,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCod
     // This method is intended for the one specific purpose of helping print "00.000E0".
     U_ASSERT(isSignificantDigits());
     U_ASSERT(value.isZero());
-    value.setFractionLength(fPrecision.fUnion.fracSig.fMinSig - minInt, INT32_MAX);
+    value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt);
 }
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
index ac5e1a4db1f6ca5e0e8f8fc89761de922498fff1..9b395082d7f5afebc7e25be9d8e0c756c3b39229 100644 (file)
@@ -77,27 +77,30 @@ void DecimalQuantityTest::checkDoubleBehavior(double d, bool explicitRequired) {
 void DecimalQuantityTest::testDecimalQuantityBehaviorStandalone() {
     UErrorCode status = U_ZERO_ERROR;
     DecimalQuantity fq;
-    assertToStringAndHealth(fq, u"<DecimalQuantity 999:0:0:-999 long 0E0>");
+    assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 0E0>");
     fq.setToInt(51423);
-    assertToStringAndHealth(fq, u"<DecimalQuantity 999:0:0:-999 long 51423E0>");
+    assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 51423E0>");
     fq.adjustMagnitude(-3);
-    assertToStringAndHealth(fq, u"<DecimalQuantity 999:0:0:-999 long 51423E-3>");
-    fq.setToLong(999999999999000L);
-    assertToStringAndHealth(fq, u"<DecimalQuantity 999:0:0:-999 long 999999999999E3>");
-    fq.setIntegerLength(2, 5);
-    assertToStringAndHealth(fq, u"<DecimalQuantity 5:2:0:-999 long 999999999999E3>");
-    fq.setFractionLength(3, 6);
-    assertToStringAndHealth(fq, u"<DecimalQuantity 5:2:-3:-6 long 999999999999E3>");
+    assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 51423E-3>");
+
+    fq.setToLong(90909090909000L);
+    assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 90909090909E3>");
+    fq.setMinInteger(2);
+    fq.applyMaxInteger(5);
+    assertToStringAndHealth(fq, u"<DecimalQuantity 2:0 long 9E3>");
+    fq.setMinFraction(3);
+    assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 9E3>");
+
     fq.setToDouble(987.654321);
-    assertToStringAndHealth(fq, u"<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
+    assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987654321E-6>");
     fq.roundToInfinity();
-    assertToStringAndHealth(fq, u"<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
+    assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987654321E-6>");
     fq.roundToIncrement(0.005, RoundingMode::UNUM_ROUND_HALFEVEN, 3, status);
     assertSuccess("Rounding to increment", status);
-    assertToStringAndHealth(fq, u"<DecimalQuantity 5:2:-3:-6 long 987655E-3>");
+    assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987655E-3>");
     fq.roundToMagnitude(-2, RoundingMode::UNUM_ROUND_HALFEVEN, status);
     assertSuccess("Rounding to magnitude", status);
-    assertToStringAndHealth(fq, u"<DecimalQuantity 5:2:-3:-6 long 98766E-2>");
+    assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 98766E-2>");
 }
 
 void DecimalQuantityTest::testSwitchStorage() {
@@ -119,6 +122,15 @@ void DecimalQuantityTest::testSwitchStorage() {
     assertFalse("Should not be using byte array", fq.isUsingBytes());
     assertEquals("Failed on round", u"1.23412341234E+16", fq.toScientificString());
     assertHealth(fq);
+    // Bytes with popFromLeft
+    fq.setToDecNumber({"999999999999999999"}, status);
+    assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 bytes 999999999999999999E0>");
+    fq.applyMaxInteger(17);
+    assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 bytes 99999999999999999E0>");
+    fq.applyMaxInteger(16);
+    assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 9999999999999999E0>");
+    fq.applyMaxInteger(15);
+    assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 999999999999999E0>");
 }
 
 void DecimalQuantityTest::testCopyMove() {
@@ -127,21 +139,21 @@ void DecimalQuantityTest::testCopyMove() {
         DecimalQuantity a;
         a.setToLong(1234123412341234L);
         DecimalQuantity b = a; // copy constructor
-        assertToStringAndHealth(a, u"<DecimalQuantity 999:0:0:-999 long 1234123412341234E0>");
-        assertToStringAndHealth(b, u"<DecimalQuantity 999:0:0:-999 long 1234123412341234E0>");
+        assertToStringAndHealth(a, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
+        assertToStringAndHealth(b, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
         DecimalQuantity c(std::move(a)); // move constructor
-        assertToStringAndHealth(c, u"<DecimalQuantity 999:0:0:-999 long 1234123412341234E0>");
+        assertToStringAndHealth(c, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
         c.setToLong(54321L);
-        assertToStringAndHealth(c, u"<DecimalQuantity 999:0:0:-999 long 54321E0>");
+        assertToStringAndHealth(c, u"<DecimalQuantity 0:0 long 54321E0>");
         c = b; // copy assignment
-        assertToStringAndHealth(b, u"<DecimalQuantity 999:0:0:-999 long 1234123412341234E0>");
-        assertToStringAndHealth(c, u"<DecimalQuantity 999:0:0:-999 long 1234123412341234E0>");
+        assertToStringAndHealth(b, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
+        assertToStringAndHealth(c, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
         b.setToLong(45678);
         c.setToLong(56789);
         c = std::move(b); // move assignment
-        assertToStringAndHealth(c, u"<DecimalQuantity 999:0:0:-999 long 45678E0>");
+        assertToStringAndHealth(c, u"<DecimalQuantity 0:0 long 45678E0>");
         a = std::move(c); // move assignment to a defunct object
-        assertToStringAndHealth(a, u"<DecimalQuantity 999:0:0:-999 long 45678E0>");
+        assertToStringAndHealth(a, u"<DecimalQuantity 0:0 long 45678E0>");
     }
 
     // Large numbers (requires byte allocation)
@@ -150,21 +162,21 @@ void DecimalQuantityTest::testCopyMove() {
         DecimalQuantity a;
         a.setToDecNumber({"1234567890123456789", -1}, status);
         DecimalQuantity b = a; // copy constructor
-        assertToStringAndHealth(a, u"<DecimalQuantity 999:0:0:-999 bytes 1234567890123456789E0>");
-        assertToStringAndHealth(b, u"<DecimalQuantity 999:0:0:-999 bytes 1234567890123456789E0>");
+        assertToStringAndHealth(a, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
+        assertToStringAndHealth(b, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
         DecimalQuantity c(std::move(a)); // move constructor
-        assertToStringAndHealth(c, u"<DecimalQuantity 999:0:0:-999 bytes 1234567890123456789E0>");
+        assertToStringAndHealth(c, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
         c.setToDecNumber({"9876543210987654321", -1}, status);
-        assertToStringAndHealth(c, u"<DecimalQuantity 999:0:0:-999 bytes 9876543210987654321E0>");
+        assertToStringAndHealth(c, u"<DecimalQuantity 0:0 bytes 9876543210987654321E0>");
         c = b; // copy assignment
-        assertToStringAndHealth(b, u"<DecimalQuantity 999:0:0:-999 bytes 1234567890123456789E0>");
-        assertToStringAndHealth(c, u"<DecimalQuantity 999:0:0:-999 bytes 1234567890123456789E0>");
+        assertToStringAndHealth(b, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
+        assertToStringAndHealth(c, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
         b.setToDecNumber({"876543210987654321", -1}, status);
         c.setToDecNumber({"987654321098765432", -1}, status);
         c = std::move(b); // move assignment
-        assertToStringAndHealth(c, u"<DecimalQuantity 999:0:0:-999 bytes 876543210987654321E0>");
+        assertToStringAndHealth(c, u"<DecimalQuantity 0:0 bytes 876543210987654321E0>");
         a = std::move(c); // move assignment to a defunct object
-        assertToStringAndHealth(a, u"<DecimalQuantity 999:0:0:-999 bytes 876543210987654321E0>");
+        assertToStringAndHealth(a, u"<DecimalQuantity 0:0 bytes 876543210987654321E0>");
     }
 }
 
@@ -364,8 +376,10 @@ void DecimalQuantityTest::testMaxDigits() {
     DecimalQuantity dq;
     dq.setToDouble(876.543);
     dq.roundToInfinity();
-    dq.setIntegerLength(0, 2);
-    dq.setFractionLength(0, 2);
+    dq.setMinInteger(0);
+    dq.applyMaxInteger(2);
+    dq.setMinFraction(0);
+    dq.roundToMagnitude(-2, UNUM_ROUND_FLOOR, status);
     assertEquals("Should trim, toPlainString", "76.54", dq.toPlainString());
     assertEquals("Should trim, toScientificString", "7.654E+1", dq.toScientificString());
     assertEquals("Should trim, toLong", 76LL, dq.toLong(true));
@@ -376,9 +390,7 @@ void DecimalQuantityTest::testMaxDigits() {
     dq.toDecNum(dn, status);
     DecimalQuantity copy;
     copy.setToDecNum(dn, status);
-    if (!logKnownIssue("13701")) {
-        assertEquals("Should trim, toDecNum", "76.54", copy.toPlainString());
-    }
+    assertEquals("Should trim, toDecNum", "76.54", copy.toPlainString());
 }
 
 void DecimalQuantityTest::testNickelRounding() {
index f9d2da5e9cbc9ad15fbff70fe6765a7858df494f..258274fdb6f86bd4f7780592c1709c657b382659 100644 (file)
@@ -27,26 +27,31 @@ import com.ibm.icu.text.UFieldPosition;
  */
 public interface DecimalQuantity extends PluralRules.IFixedDecimal {
     /**
-     * Sets the minimum and maximum integer digits that this {@link DecimalQuantity} should generate.
+     * 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.
-     * @param maxInt
-     *            The maximum number of integer digits.
      */
-    public void setIntegerLength(int minInt, int maxInt);
+    public void setMinInteger(int minInt);
 
     /**
-     * Sets the minimum and maximum fraction digits that this {@link DecimalQuantity} should generate.
+     * Sets the minimum fraction digits that this {@link DecimalQuantity} should generate.
      * This method does not perform rounding.
      *
      * @param minFrac
      *            The minimum number of fraction digits.
-     * @param maxFrac
-     *            The maximum number of fraction digits.
      */
-    public void setFractionLength(int minFrac, int maxFrac);
+    public void setMinFraction(int minFrac);
+
+    /**
+     * Truncates digits from the upper magnitude of the number in order to satisfy the
+     * specified maximum number of integer digits.
+     *
+     * @param maxInt
+     *            The maximum number of integer digits.
+     */
+    public void applyMaxInteger(int maxInt);
 
     /**
      * Rounds the number to a specified interval, such as 0.05.
index 138c2e9acf7af823d07c2e94dc22ec07047bff93..e8f9f2eda41a8a63f7168d587883d50ff21205d3 100644 (file)
@@ -79,45 +79,18 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
      */
     protected boolean isApproximate;
 
-    // Four positions: left optional '(', left required '[', right required ']', right optional ')'.
-    // These four positions determine which digits are displayed in the output string. They do NOT
-    // affect rounding. These positions are internal-only and can be specified only by the public
-    // endpoints like setFractionLength, setIntegerLength, and setSignificantDigits, among others.
-    //
-    // * Digits between lReqPos and rReqPos are in the "required zone" and are always displayed.
-    // * Digits between lOptPos and rOptPos but outside the required zone are in the "optional zone"
-    // and are displayed unless they are trailing off the left or right edge of the number and
-    // have a numerical value of zero. In order to be "trailing", the digits need to be beyond
-    // the decimal point in their respective directions.
-    // * Digits outside of the "optional zone" are never displayed.
-    //
-    // See the table below for illustrative examples.
-    //
-    // +---------+---------+---------+---------+------------+------------------------+--------------+
-    // | lOptPos | lReqPos | rReqPos | rOptPos |   number   |        positions       | en-US string |
-    // +---------+---------+---------+---------+------------+------------------------+--------------+
-    // |    5    |    2    |   -1    |   -5    |   1234.567 |     ( 12[34.5]67  )    |   1,234.567  |
-    // |    3    |    2    |   -1    |   -5    |   1234.567 |      1(2[34.5]67  )    |     234.567  |
-    // |    3    |    2    |   -1    |   -2    |   1234.567 |      1(2[34.5]6)7      |     234.56   |
-    // |    6    |    4    |    2    |   -5    | 123456789. |  123(45[67]89.     )   | 456,789.     |
-    // |    6    |    4    |    2    |    1    | 123456789. |     123(45[67]8)9.     | 456,780.     |
-    // |   -1    |   -1    |   -3    |   -4    | 0.123456   |     0.1([23]4)56       |        .0234 |
-    // |    6    |    4    |   -2    |   -2    |     12.3   |     (  [  12.3 ])      |    0012.30   |
-    // +---------+---------+---------+---------+------------+------------------------+--------------+
-    //
-    protected int lOptPos = Integer.MAX_VALUE;
+    // Positions to keep track of leading and trailing zeros.
+    // lReqPos is the magnitude of the first required leading zero.
+    // rReqPos is the magnitude of the last required trailing zero.
     protected int lReqPos = 0;
     protected int rReqPos = 0;
-    protected int rOptPos = Integer.MIN_VALUE;
 
     @Override
     public void copyFrom(DecimalQuantity _other) {
         copyBcdFrom(_other);
         DecimalQuantity_AbstractBCD other = (DecimalQuantity_AbstractBCD) _other;
-        lOptPos = other.lOptPos;
         lReqPos = other.lReqPos;
         rReqPos = other.rReqPos;
-        rOptPos = other.rOptPos;
         scale = other.scale;
         precision = other.precision;
         flags = other.flags;
@@ -127,20 +100,17 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
     }
 
     public DecimalQuantity_AbstractBCD clear() {
-        lOptPos = Integer.MAX_VALUE;
         lReqPos = 0;
         rReqPos = 0;
-        rOptPos = Integer.MIN_VALUE;
         flags = 0;
         setBcdToZero(); // sets scale, precision, hasDouble, origDouble, origDelta, and BCD data
         return this;
     }
 
     @Override
-    public void setIntegerLength(int minInt, int maxInt) {
+    public void setMinInteger(int minInt) {
         // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class.
         assert minInt >= 0;
-        assert maxInt >= minInt;
 
         // 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.
@@ -149,30 +119,40 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
         }
 
         // Save values into internal state
-        // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
-        lOptPos = maxInt;
         lReqPos = minInt;
     }
 
     @Override
-    public void setFractionLength(int minFrac, int maxFrac) {
+    public void setMinFraction(int minFrac) {
         // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class.
         assert minFrac >= 0;
-        assert maxFrac >= minFrac;
 
         // Save values into internal state
         // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
         rReqPos = -minFrac;
-        rOptPos = -maxFrac;
+    }
+
+    @Override
+    public void applyMaxInteger(int maxInt) {
+        // Validation should happen outside of DecimalQuantity, e.g., in the Precision class.
+        assert maxInt >= 0;
+
+        if (precision == 0) {
+            return;
+        }
+
+        int magnitude = getMagnitude();
+        if (maxInt <= magnitude) {
+            popFromLeft(magnitude - maxInt + 1);
+            compact();
+        }
     }
 
     @Override
     public long getPositionFingerprint() {
         long fingerprint = 0;
-        fingerprint ^= lOptPos;
         fingerprint ^= (lReqPos << 16);
         fingerprint ^= ((long) rReqPos << 32);
-        fingerprint ^= ((long) rOptPos << 48);
         return fingerprint;
     }
 
@@ -276,7 +256,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
         assert !isApproximate;
 
         int magnitude = scale + precision;
-        int result = (lReqPos > magnitude) ? lReqPos : (lOptPos < magnitude) ? lOptPos : magnitude;
+        int result = (lReqPos > magnitude) ? lReqPos : magnitude;
         return result - 1;
     }
 
@@ -287,7 +267,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
         assert !isApproximate;
 
         int magnitude = scale;
-        int result = (rReqPos < magnitude) ? rReqPos : (rOptPos > magnitude) ? rOptPos : magnitude;
+        int result = (rReqPos < magnitude) ? rReqPos : magnitude;
         return result;
     }
 
@@ -586,7 +566,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
         // Fallback behavior upon truncateIfOverflow is to truncate at 17 digits.
         assert(truncateIfOverflow || fitsInLong());
         long result = 0L;
-        int upperMagnitude = Math.min(scale + precision, lOptPos) - 1;
+        int upperMagnitude = scale + precision - 1;
         if (truncateIfOverflow) {
             upperMagnitude = Math.min(upperMagnitude, 17);
         }
@@ -607,7 +587,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
     public long toFractionLong(boolean includeTrailingZeros) {
         long result = 0L;
         int magnitude = -1;
-        int lowerMagnitude = Math.max(scale, rOptPos);
+        int lowerMagnitude = scale;
         if (includeTrailingZeros) {
             lowerMagnitude = Math.min(lowerMagnitude, rReqPos);
         }
@@ -1055,8 +1035,8 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
         }
         // NOTE: It is not safe to add to lOptPos (aka maxInt) or subtract from
         // rOptPos (aka -maxFrac) due to overflow.
-        int upperPos = Math.min(precision + scale, lOptPos) - scale - 1;
-        int lowerPos = Math.max(scale, rOptPos) - scale;
+        int upperPos = precision - 1;
+        int lowerPos = 0;
         int p = upperPos;
         result.append((char) ('0' + getDigitPos(p)));
         if ((--p) >= lowerPos) {
@@ -1105,10 +1085,8 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
                 scale == _other.scale
                 && precision == _other.precision
                 && flags == _other.flags
-                && lOptPos == _other.lOptPos
                 && lReqPos == _other.lReqPos
                 && rReqPos == _other.rReqPos
-                && rOptPos == _other.rOptPos
                 && isApproximate == _other.isApproximate;
         if (!basicEquals) {
             return false;
@@ -1169,6 +1147,14 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
      */
     protected abstract void shiftRight(int numDigits);
 
+    /**
+     * Directly removes digits from the front of the BCD list.
+     * Updates precision.
+     *
+     * CAUTION: it is the caller's responsibility to call {@link #compact} after this method.
+     */
+    protected abstract void popFromLeft(int numDigits);
+
     /**
      * Sets the internal representation to zero. Clears any values stored in scale, precision, hasDouble,
      * origDouble, origDelta, and BCD data.
index faa46281a1d6c5f8dd05d2281ef6d383f1be10fa..791538c9386f6a15383a7f4fbf96cef679af2d7b 100644 (file)
@@ -154,6 +154,19 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
         precision -= numDigits;
     }
 
+    @Override
+    protected void popFromLeft(int numDigits) {
+        if (usingBytes) {
+            int i = precision - 1;
+            for (; i >= precision - numDigits; i--) {
+                bcdBytes[i] = 0;
+            }
+        } else {
+            bcdLong &= (1L << ((precision - numDigits) * 4)) - 1;
+        }
+        precision -= numDigits;
+    }
+
     @Override
     protected void setBcdToZero() {
         if (usingBytes) {
@@ -425,11 +438,9 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
 
     @Override
     public String toString() {
-        return String.format("<DecimalQuantity %s:%d:%d:%s %s %s%s>",
-                (lOptPos > 1000 ? "999" : String.valueOf(lOptPos)),
+        return String.format("<DecimalQuantity %d:%d %s %s%s>",
                 lReqPos,
                 rReqPos,
-                (rOptPos < -1000 ? "-999" : String.valueOf(rOptPos)),
                 (usingBytes ? "bytes" : "long"),
                 (isNegative() ? "-" : ""),
                 toNumberString());
index 11f590f56c4fc9900fa08bf14004b83d6d6e6b8f..17fbe912d8b077c4bc8f3f0129e5adf0e9d59c95 100644 (file)
@@ -99,9 +99,10 @@ class NumberFormatterImpl {
         MicroProps micros = microPropsGenerator.processQuantity(inValue);
         micros.rounder.apply(inValue);
         if (micros.integerWidth.maxInt == -1) {
-            inValue.setIntegerLength(micros.integerWidth.minInt, Integer.MAX_VALUE);
+            inValue.setMinInteger(micros.integerWidth.minInt);
         } else {
-            inValue.setIntegerLength(micros.integerWidth.minInt, micros.integerWidth.maxInt);
+            inValue.setMinInteger(micros.integerWidth.minInt);
+            inValue.applyMaxInteger(micros.integerWidth.maxInt);
         }
         return micros;
     }
@@ -111,9 +112,10 @@ class NumberFormatterImpl {
         MicroProps micros = microPropsGenerator.processQuantity(inValue);
         micros.rounder.apply(inValue);
         if (micros.integerWidth.maxInt == -1) {
-            inValue.setIntegerLength(micros.integerWidth.minInt, Integer.MAX_VALUE);
+            inValue.setMinInteger(micros.integerWidth.minInt);
         } else {
-            inValue.setIntegerLength(micros.integerWidth.minInt, micros.integerWidth.maxInt);
+            inValue.setMinInteger(micros.integerWidth.minInt);
+            inValue.applyMaxInteger(micros.integerWidth.maxInt);
         }
         return micros;
     }
index 375b535b90ed45a75f85bd75afa74e67f905b7b6..5e526ebe5fc72db2b41a87d8d6b69a04cd0760f4 100644 (file)
@@ -609,7 +609,7 @@ public abstract class Precision implements Cloneable {
         @Override
         public void apply(DecimalQuantity value) {
             value.roundToInfinity();
-            value.setFractionLength(0, Integer.MAX_VALUE);
+            value.setMinFraction(0);
         }
     }
 
@@ -625,8 +625,7 @@ public abstract class Precision implements Cloneable {
         @Override
         public void apply(DecimalQuantity value) {
             value.roundToMagnitude(getRoundingMagnitudeFraction(maxFrac), mathContext);
-            value.setFractionLength(Math.max(0, -getDisplayMagnitudeFraction(minFrac)),
-                    Integer.MAX_VALUE);
+            value.setMinFraction(Math.max(0, -getDisplayMagnitudeFraction(minFrac)));
         }
     }
 
@@ -642,11 +641,10 @@ public abstract class Precision implements Cloneable {
         @Override
         public void apply(DecimalQuantity value) {
             value.roundToMagnitude(getRoundingMagnitudeSignificant(value, maxSig), mathContext);
-            value.setFractionLength(Math.max(0, -getDisplayMagnitudeSignificant(value, minSig)),
-                    Integer.MAX_VALUE);
+            value.setMinFraction(Math.max(0, -getDisplayMagnitudeSignificant(value, minSig)));
             // Make sure that digits are displayed on zero.
             if (value.isZero() && minSig > 0) {
-                value.setIntegerLength(1, Integer.MAX_VALUE);
+                value.setMinInteger(1);
             }
         }
 
@@ -656,7 +654,7 @@ public abstract class Precision implements Cloneable {
          */
         public void apply(DecimalQuantity quantity, int minInt) {
             assert quantity.isZero();
-            quantity.setFractionLength(minSig - minInt, Integer.MAX_VALUE);
+            quantity.setMinFraction(minSig - minInt);
         }
     }
 
@@ -687,7 +685,7 @@ public abstract class Precision implements Cloneable {
                 roundingMag = Math.min(roundingMag, candidate);
             }
             value.roundToMagnitude(roundingMag, mathContext);
-            value.setFractionLength(Math.max(0, -displayMag), Integer.MAX_VALUE);
+            value.setMinFraction(Math.max(0, -displayMag));
         }
     }
 
@@ -701,7 +699,7 @@ public abstract class Precision implements Cloneable {
         @Override
         public void apply(DecimalQuantity value) {
             value.roundToIncrement(increment, mathContext);
-            value.setFractionLength(increment.scale(), increment.scale());
+            value.setMinFraction(increment.scale());
         }
     }
 
index 54f5cec9ad52ca223703e1aa4103ad0c04cff330..ea84162a983e1862d308f32011fe5464709d891f 100644 (file)
@@ -81,6 +81,12 @@ public final class DecimalQuantity_64BitBCD extends DecimalQuantity_AbstractBCD
     precision -= numDigits;
   }
 
+  @Override
+  protected void popFromLeft(int numDigits) {
+      bcd &= (1L << ((precision - numDigits) * 4)) - 1;
+      precision -= numDigits;
+  }
+
   @Override
   protected void setBcdToZero() {
     bcd = 0L;
@@ -173,11 +179,9 @@ public final class DecimalQuantity_64BitBCD extends DecimalQuantity_AbstractBCD
   @Override
   public String toString() {
     return String.format(
-        "<DecimalQuantity2 %s:%d:%d:%s %016XE%d>",
-        (lOptPos > 1000 ? "max" : String.valueOf(lOptPos)),
+        "<DecimalQuantity2 %d:%d %016XE%d>",
         lReqPos,
         rReqPos,
-        (rOptPos < -1000 ? "min" : String.valueOf(rOptPos)),
         bcd,
         scale);
   }
index 7012e845253fd867e62f2b21953950310e7c13bf..de87080b796021ba8c4fedd64c48c244eea56ea3 100644 (file)
@@ -93,6 +93,15 @@ public final class DecimalQuantity_ByteArrayBCD extends DecimalQuantity_Abstract
     precision -= numDigits;
   }
 
+  @Override
+  protected void popFromLeft(int numDigits) {
+    int i = precision - 1;
+    for (; i >= precision - numDigits; i--) {
+      bcd[i] = 0;
+    }
+    precision -= numDigits;
+  }
+
   @Override
   protected void setBcdToZero() {
     for (int i = 0; i < precision; i++) {
@@ -221,11 +230,9 @@ public final class DecimalQuantity_ByteArrayBCD extends DecimalQuantity_Abstract
       sb.append(bcd[i]);
     }
     return String.format(
-        "<DecimalQuantity3 %s:%d:%d:%s %s%s%d>",
-        (lOptPos > 1000 ? "max" : String.valueOf(lOptPos)),
+        "<DecimalQuantity3 %d:%d %s%s%d>",
         lReqPos,
         rReqPos,
-        (rOptPos < -1000 ? "min" : String.valueOf(rOptPos)),
         sb,
         "E",
         scale);
index 8a86b802fc4d60217105e0630c544ccac963a46a..994ca998e2c0f7e49a2fd4c5f72e66ca2aaea031 100644 (file)
@@ -319,37 +319,39 @@ public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
   }
 
   @Override
-  public void setIntegerLength(int minInt, int maxInt) {
+  public void setMinInteger(int minInt) {
     // Graceful failures for bogus input
     minInt = Math.max(0, minInt);
-    maxInt = Math.max(0, maxInt);
-
-    // The minima must be less than or equal to the maxima
-    if (maxInt < minInt) {
-      minInt = maxInt;
-    }
 
     // Save values into internal state
     // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
-    lOptPos = maxInt;
     lReqPos = minInt;
   }
 
   @Override
-  public void setFractionLength(int minFrac, int maxFrac) {
+  public void setMinFraction(int minFrac) {
     // Graceful failures for bogus input
     minFrac = Math.max(0, minFrac);
-    maxFrac = Math.max(0, maxFrac);
-
-    // The minima must be less than or equal to the maxima
-    if (maxFrac < minFrac) {
-      minFrac = maxFrac;
-    }
 
     // Save values into internal state
     // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
     rReqPos = -minFrac;
-    rOptPos = -maxFrac;
+  }
+
+  @Override
+  public void applyMaxInteger(int maxInt) {
+    BigDecimal d;
+    if (primary != -1) {
+      d = BigDecimal.valueOf(primary).scaleByPowerOfTen(primaryScale);
+    } else {
+      d = fallback;
+    }
+    d = d.scaleByPowerOfTen(-maxInt).remainder(BigDecimal.ONE).scaleByPowerOfTen(maxInt);
+    if (primary != -1) {
+      primary = d.scaleByPowerOfTen(-primaryScale).longValueExact();
+    } else {
+      fallback = d;
+    }
   }
 
   @Override
index e8ec189af9bfc1d58e195625bd8c61f0e51d46d7..faaa9b3cf48403caf9e02294c850883ee0864f33 100644 (file)
@@ -168,8 +168,8 @@ public class DecimalQuantityTest extends TestFmwk {
         DecimalQuantity q0 = rq.createCopy();
         // Force an accurate double
         q0.roundToInfinity();
-        q0.setIntegerLength(1, Integer.MAX_VALUE);
-        q0.setFractionLength(1, Integer.MAX_VALUE);
+        q0.setMinInteger(1);
+        q0.setMinFraction(1);
         String actual = q0.toPlainString();
         assertEquals("Unexpected output from simple string conversion (" + q0 + ")", expected, actual);
     }
@@ -305,6 +305,15 @@ public class DecimalQuantityTest extends TestFmwk {
         assertFalse("Should not be using byte array", fq.isUsingBytes());
         assertEquals("Failed on round", "1.23412341234E+16", fq.toScientificString());
         assertNull("Failed health check", fq.checkHealth());
+        // Bytes with popFromLeft
+        fq.setToBigDecimal(new BigDecimal("999999999999999999"));
+        assertToStringAndHealth(fq, "<DecimalQuantity 0:0 bytes 999999999999999999E0>");
+        fq.applyMaxInteger(17);
+        assertToStringAndHealth(fq, "<DecimalQuantity 0:0 bytes 99999999999999999E0>");
+        fq.applyMaxInteger(16);
+        assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 9999999999999999E0>");
+        fq.applyMaxInteger(15);
+        assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 999999999999999E0>");
     }
 
     @Test
@@ -389,25 +398,26 @@ public class DecimalQuantityTest extends TestFmwk {
     @Test
     public void testDecimalQuantityBehaviorStandalone() {
         DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
-        assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 0E0>");
+        assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 0E0>");
         fq.setToInt(51423);
-        assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 51423E0>");
+        assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 51423E0>");
         fq.adjustMagnitude(-3);
-        assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 51423E-3>");
-        fq.setToLong(999999999999000L);
-        assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 999999999999E3>");
-        fq.setIntegerLength(2, 5);
-        assertToStringAndHealth(fq, "<DecimalQuantity 5:2:0:-999 long 999999999999E3>");
-        fq.setFractionLength(3, 6);
-        assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 999999999999E3>");
+        assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 51423E-3>");
+        fq.setToLong(90909090909000L);
+        assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 90909090909E3>");
+        fq.setMinInteger(2);
+        fq.applyMaxInteger(5);
+        assertToStringAndHealth(fq, "<DecimalQuantity 2:0 long 9E3>");
+        fq.setMinFraction(3);
+        assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 9E3>");
         fq.setToDouble(987.654321);
-        assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
+        assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 987654321E-6>");
         fq.roundToInfinity();
-        assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
+        assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 987654321E-6>");
         fq.roundToIncrement(new BigDecimal("0.005"), MATH_CONTEXT_HALF_EVEN);
-        assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987655E-3>");
+        assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 987655E-3>");
         fq.roundToMagnitude(-2, MATH_CONTEXT_HALF_EVEN);
-        assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 98766E-2>");
+        assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 98766E-2>");
     }
 
     @Test
@@ -501,8 +511,10 @@ public class DecimalQuantityTest extends TestFmwk {
     public void testMaxDigits() {
         DecimalQuantity_DualStorageBCD dq = new DecimalQuantity_DualStorageBCD(876.543);
         dq.roundToInfinity();
-        dq.setIntegerLength(0, 2);
-        dq.setFractionLength(0, 2);
+        dq.setMinInteger(0);
+        dq.applyMaxInteger(2);
+        dq.setMinFraction(0);
+        dq.roundToMagnitude(-2, RoundingUtils.mathContextUnlimited(RoundingMode.FLOOR));
         assertEquals("Should trim, toPlainString", "76.54", dq.toPlainString());
         assertEquals("Should trim, toScientificString", "7.654E+1", dq.toScientificString());
         assertEquals("Should trim, toLong", 76, dq.toLong(true));