}
}
+DecimalQuantity DecimalQuantity::fromExponentString(UnicodeString num, UErrorCode& status) {
+ if (num.indexOf(u'e') >= 0 || num.indexOf(u'c') >= 0
+ || num.indexOf(u'E') >= 0 || num.indexOf(u'C') >= 0) {
+ int32_t ePos = num.lastIndexOf('e');
+ if (ePos < 0) {
+ ePos = num.lastIndexOf('c');
+ }
+ if (ePos < 0) {
+ ePos = num.lastIndexOf('E');
+ }
+ if (ePos < 0) {
+ ePos = num.lastIndexOf('C');
+ }
+ int32_t expNumPos = ePos + 1;
+ UnicodeString exponentStr = num.tempSubString(expNumPos, num.length() - expNumPos);
+
+ // parse exponentStr into exponent, but note that parseAsciiInteger doesn't handle the minus sign
+ bool isExpStrNeg = num[expNumPos] == u'-';
+ int32_t exponentParsePos = isExpStrNeg ? 1 : 0;
+ int32_t exponent = ICU_Utility::parseAsciiInteger(exponentStr, exponentParsePos);
+ exponent = isExpStrNeg ? -exponent : exponent;
+
+ // Compute the decNumber representation
+ UnicodeString fractionStr = num.tempSubString(0, ePos);
+ CharString fracCharStr = CharString();
+ fracCharStr.appendInvariantChars(fractionStr, status);
+ DecNum decnum;
+ decnum.setTo(fracCharStr.toStringPiece(), status);
+
+ // Clear and set this DecimalQuantity instance
+ DecimalQuantity dq;
+ dq.setToDecNum(decnum, status);
+ int32_t numFracDigit = getVisibleFractionCount(fractionStr);
+ dq.setMinFraction(numFracDigit);
+ dq.adjustExponent(exponent);
+
+ return dq;
+ } else {
+ DecimalQuantity dq;
+ int numFracDigit = getVisibleFractionCount(num);
+
+ CharString numCharStr = CharString();
+ numCharStr.appendInvariantChars(num, status);
+ dq.setToDecNumber(numCharStr.toStringPiece(), status);
+
+ dq.setMinFraction(numFracDigit);
+ return dq;
+ }
+}
+
+int32_t DecimalQuantity::getVisibleFractionCount(UnicodeString value) {
+ int decimalPos = value.indexOf('.') + 1;
+ if (decimalPos == 0) {
+ return 0;
+ } else {
+ return value.length() - decimalPos;
+ }
+}
+
int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const {
// NOTE: Call sites should be guarded by fitsInLong(), like this:
// if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ }
return sb;
}
+
+UnicodeString DecimalQuantity::toExponentString() const {
+ U_ASSERT(!isApproximate);
+ UnicodeString sb;
+ if (isNegative()) {
+ sb.append(u'-');
+ }
+
+ int32_t upper = scale + precision - 1;
+ int32_t lower = scale;
+ if (upper < lReqPos - 1) {
+ upper = lReqPos - 1;
+ }
+ if (lower > rReqPos) {
+ lower = rReqPos;
+ }
+ int32_t p = upper;
+ if (p < 0) {
+ sb.append(u'0');
+ }
+ for (; p >= 0; p--) {
+ sb.append(u'0' + getDigitPos(p - scale));
+ }
+ if (lower < 0) {
+ sb.append(u'.');
+ }
+ for(; p >= lower; p--) {
+ sb.append(u'0' + getDigitPos(p - scale));
+ }
+
+ if (exponent != 0) {
+ sb.append(u'c');
+ ICU_Utility::appendNumber(sb, exponent);
+ }
+
+ return sb;
+}
+
UnicodeString DecimalQuantity::toScientificString() const {
U_ASSERT(!isApproximate);
UnicodeString result;
/** Internal method if the caller already has a DecNum. */
DecimalQuantity &setToDecNum(const DecNum& n, UErrorCode& status);
+ /** Returns a DecimalQuantity after parsing the input string. */
+ static DecimalQuantity fromExponentString(UnicodeString n, UErrorCode& status);
+
/**
* Appends a digit, optionally with one or more leading zeros, to the end of the value represented
* by this DecimalQuantity.
/** Returns the string without exponential notation. Slightly slower than toScientificString(). */
UnicodeString toPlainString() const;
+ /** Returns the string using ASCII digits and using exponential notation for non-zero
+ exponents, following the UTS 35 specification for plural rule samples. */
+ UnicodeString toExponentString() const;
+
/** Visible for testing */
inline bool isUsingBytes() { return usingBytes; }
void _setToDecNum(const DecNum& dn, UErrorCode& status);
+ static int32_t getVisibleFractionCount(UnicodeString value);
+
void convertToAccurateDouble();
/** Ensure that a byte array of at least 40 digits is allocated. */
void testNickelRounding();
void testScientificAndCompactSuppressedExponent();
void testSuppressedExponentUnchangedByInitialScaling();
+ void testDecimalQuantityParseFormatRoundTrip();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0) override;
TESTCASE_AUTO(testNickelRounding);
TESTCASE_AUTO(testScientificAndCompactSuppressedExponent);
TESTCASE_AUTO(testSuppressedExponentUnchangedByInitialScaling);
+ TESTCASE_AUTO(testDecimalQuantityParseFormatRoundTrip);
TESTCASE_AUTO_END;
}
}
}
+
+void DecimalQuantityTest::testDecimalQuantityParseFormatRoundTrip() {
+ IcuTestErrorCode status(*this, "testDecimalQuantityParseFormatRoundTrip");
+
+ struct TestCase {
+ UnicodeString numberString;
+ } cases[] = {
+ // number string
+ { u"0" },
+ { u"1" },
+ { u"1.0" },
+ { u"1.00" },
+ { u"1.1" },
+ { u"1.10" },
+ { u"-1.10" },
+ { u"0.0" },
+ { u"1c5" },
+ { u"1.0c5" },
+ { u"1.1c5" },
+ { u"1.10c5" },
+ { u"0.00" },
+ { u"0.1" },
+ { u"1c-1" },
+ { u"1.0c-1" }
+ };
+
+ for (const auto& cas : cases) {
+ UnicodeString numberString = cas.numberString;
+
+ DecimalQuantity dq = DecimalQuantity::fromExponentString(numberString, status);
+ UnicodeString roundTrip = dq.toExponentString();
+
+ assertEquals("DecimalQuantity format(parse(s)) should equal original s", numberString, roundTrip);
+ }
+
+ DecimalQuantity dq = DecimalQuantity::fromExponentString(u"1c0", status);
+ assertEquals("Zero ignored for visible exponent",
+ u"1",
+ dq.toExponentString());
+
+ dq.clear();
+ dq = DecimalQuantity::fromExponentString(u"1.0c0", status);
+ assertEquals("Zero ignored for visible exponent",
+ u"1.0",
+ dq.toExponentString());
+
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */
*/
public String toPlainString();
+ /**
+ * Returns the string using ASCII digits and using exponential notation for non-zero
+ * exponents, following the UTS 35 specification for plural rule samples.
+ */
+ public String toExponentString();
+
/**
* Like clone, but without the restrictions of the Cloneable interface clone.
*
}
}
+ @Override
+ public String toExponentString() {
+ StringBuilder sb = new StringBuilder();
+ toExponentString(sb);
+ return sb.toString();
+ }
+
+ private void toExponentString(StringBuilder result) {
+ assert(!isApproximate);
+ if (isNegative()) {
+ result.append('-');
+ }
+
+ int upper = scale + precision - 1;
+ int lower = scale;
+ if (upper < lReqPos - 1) {
+ upper = lReqPos - 1;
+ }
+ if (lower > rReqPos) {
+ lower = rReqPos;
+ }
+
+ int p = upper;
+ if (p < 0) {
+ result.append('0');
+ }
+ for (; p >= 0; p--) {
+ result.append((char) ('0' + getDigitPos(p - scale)));
+ }
+ if (lower < 0) {
+ result.append('.');
+ }
+ for(; p >= lower; p--) {
+ result.append((char) ('0' + getDigitPos(p - scale)));
+ }
+
+ if (exponent != 0) {
+ result.append('c');
+ result.append(exponent);
+ }
+ }
+
@Override
public boolean equals(Object other) {
if (this == other) {
return new DecimalQuantity_DualStorageBCD(this);
}
+ /**
+ * Returns a DecimalQuantity after parsing the input string.
+ *
+ * @param s The String to parse
+ */
+ public static DecimalQuantity fromExponentString(String num) {
+ if (num.contains("e") || num.contains("c")
+ || num.contains("E") || num.contains("C")) {
+ int ePos = num.lastIndexOf('e');
+ if (ePos < 0) {
+ ePos = num.lastIndexOf('c');
+ }
+ if (ePos < 0) {
+ ePos = num.lastIndexOf('E');
+ }
+ if (ePos < 0) {
+ ePos = num.lastIndexOf('C');
+ }
+ int expNumPos = ePos + 1;
+ String exponentStr = num.substring(expNumPos);
+ int exponent = Integer.parseInt(exponentStr);
+
+ String fractionStr = num.substring(0, ePos);
+ BigDecimal fraction = new BigDecimal(fractionStr);
+
+ DecimalQuantity_DualStorageBCD dq = new DecimalQuantity_DualStorageBCD(fraction);
+ int numFracDigit = getVisibleFractionCount(fractionStr);
+ dq.setMinFraction(numFracDigit);
+ dq.adjustExponent(exponent);
+
+ return dq;
+ } else {
+ int numFracDigit = getVisibleFractionCount(num);
+ DecimalQuantity_DualStorageBCD dq = new DecimalQuantity_DualStorageBCD(new BigDecimal(num));
+ dq.setMinFraction(numFracDigit);
+ return dq;
+ }
+ }
+
+ private static int getVisibleFractionCount(String value) {
+ int decimalPos = value.indexOf('.') + 1;
+ if (decimalPos == 0) {
+ return 0;
+ } else {
+ return value.length() - decimalPos;
+ }
+ }
+
@Override
protected byte getDigitPos(int position) {
if (usingBytes) {
public boolean isHasIntegerValue() {
return scaleBigDecimal(toBigDecimal()) >= 0;
}
+
+ @Override public String toExponentString() {
+ return this.toPlainString();
+ }
}
}
}
+ @Test
+ public void testDecimalQuantityParseFormatRoundTrip() {
+ Object[] casesData = {
+ // number string
+ "0",
+ "1",
+ "1.0",
+ "1.00",
+ "1.1",
+ "1.10",
+ "-1.10",
+ "0.0",
+ "1c5",
+ "1.0c5",
+ "1.1c5",
+ "1.10c5",
+ "0.00",
+ "0.1",
+ "1c-1",
+ "1.0c-1"
+ };
+
+ for (Object caseDatum : casesData) {
+ String numStr = (String) caseDatum;
+ DecimalQuantity dq = DecimalQuantity_DualStorageBCD.fromExponentString(numStr);
+ String roundTrip = dq.toExponentString();
+
+ assertEquals("DecimalQuantity format(parse(s)) should equal original s", numStr, roundTrip);
+ }
+
+ assertEquals("Zero ignored for visible exponent",
+ "1",
+ DecimalQuantity_DualStorageBCD.fromExponentString("1c0").toExponentString());
+ assertEquals("Zero ignored for visible exponent",
+ "1.0",
+ DecimalQuantity_DualStorageBCD.fromExponentString("1.0c0").toExponentString());
+ }
+
static boolean doubleEquals(double d1, double d2) {
return (Math.abs(d1 - d2) < 1e-6) || (Math.abs((d1 - d2) / d1) < 1e-6);
}