mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 22:44:49 +00:00
ICU-13701 Refactoring DecimalQuantity: removing lOptPos/rOptPos.
Combined ICU4C and ICU4J.
This commit is contained in:
parent
59006770ed
commit
a1cc16ccd3
14 changed files with 261 additions and 218 deletions
|
@ -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()),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Add table
Reference in a new issue