ICU-13634 Fixing resolution of negative and percent signs in parsing; adding custom sign support to ScientificMatcher; and other minor fixes.

X-SVN-Rev: 41180
This commit is contained in:
Shane Carr 2018-03-31 03:10:44 +00:00
parent db9c74b3f4
commit 7f9de6f1db
24 changed files with 228 additions and 102 deletions

View file

@ -298,6 +298,7 @@ int32_t DecimalFormat::getAttribute(UNumberFormatAttribute attr, UErrorCode& sta
}
void DecimalFormat::setGroupingUsed(UBool enabled) {
NumberFormat::setGroupingUsed(enabled); // to set field for compatibility
if (enabled) {
// Set to a reasonable default value
fProperties->groupingSize = 3;
@ -649,6 +650,7 @@ ERoundingMode DecimalFormat::getRoundingMode(void) const {
}
void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) {
NumberFormat::setMaximumIntegerDigits(roundingMode); // to set field for compatibility
fProperties->roundingMode = static_cast<UNumberFormatRoundingMode>(roundingMode);
refreshFormatterNoError();
}
@ -985,6 +987,12 @@ void DecimalFormat::refreshFormatter(UErrorCode& status) {
fParserWithCurrency.adoptInsteadAndCheckErrorCode(
NumberParserImpl::createParserFromProperties(
*fProperties, *fSymbols, true, status), status);
// In order for the getters to work, we need to populate some fields in NumberFormat.
NumberFormat::setMaximumIntegerDigits(fExportedProperties->maximumIntegerDigits);
NumberFormat::setMinimumIntegerDigits(fExportedProperties->minimumIntegerDigits);
NumberFormat::setMaximumFractionDigits(fExportedProperties->maximumFractionDigits);
NumberFormat::setMinimumFractionDigits(fExportedProperties->minimumFractionDigits);
}
void DecimalFormat::refreshFormatterNoError() {

View file

@ -485,7 +485,9 @@ int64_t DecimalQuantity::toLong() const {
for (int32_t magnitude = scale + precision - 1; magnitude >= 0; magnitude--) {
result = result * 10 + getDigitPos(magnitude - scale);
}
if (isNegative()) { result = -result; }
if (isNegative()) {
result = -result;
}
return result;
}
@ -544,7 +546,9 @@ double DecimalQuantity::toDouble() const {
UnicodeString numberString = toNumberString();
int32_t count;
double result = converter.StringToDouble(reinterpret_cast<const uint16_t*>(numberString.getBuffer()), numberString.length(), &count);
if (isNegative()) { result = -result; }
if (isNegative()) {
result = -result;
}
return result;
}
@ -560,7 +564,9 @@ double DecimalQuantity::toDoubleFromOriginal() const {
for (; delta <= -22; delta += 22) result /= 1e22;
result /= DOUBLE_MULTIPLIERS[-delta];
}
if (isNegative()) { result *= -1; }
if (isNegative()) {
result = -result;
}
return result;
}
@ -1080,6 +1086,9 @@ UnicodeString DecimalQuantity::toString() const {
UnicodeString DecimalQuantity::toNumberString() const {
UnicodeString result;
if (precision == 0) {
result.append(u'0');
}
for (int32_t i = 0; i < precision; i++) {
result.append(u'0' + getDigitPos(precision - i - 1));
}

View file

@ -95,7 +95,7 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
*/
void multiplyBy(int32_t multiplicand);
/** Flips the sign from positive to negative and back. C++-only: not currently needed in Java. */
/** Flips the sign from positive to negative and back. */
void negate();
/**

View file

@ -340,7 +340,7 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt
continue;
}
// Flags for setting in the ParsedNumber
// Flags for setting in the ParsedNumber; the token matchers may add more.
int flags = (signum == -1) ? FLAG_NEGATIVE : 0;
// Note: it is indeed possible for posPrefix and posSuffix to both be null.
@ -438,6 +438,12 @@ void AffixMatcher::postProcess(ParsedNumber& result) const {
result.suffix = UnicodeString();
}
result.flags |= fFlags;
if (fPrefix != nullptr) {
fPrefix->postProcess(result);
}
if (fSuffix != nullptr) {
fSuffix->postProcess(result);
}
}
}

View file

@ -233,6 +233,7 @@ void NumberParserImpl::parse(const UnicodeString& input, int32_t start, bool gre
for (int32_t i = 0; i < fNumMatchers; i++) {
fMatchers[i]->postProcess(result);
}
result.postProcess();
}
void NumberParserImpl::parseGreedyRecursive(StringSegment& segment, ParsedNumber& result,

View file

@ -37,6 +37,18 @@ void ParsedNumber::setCharsConsumed(const StringSegment& segment) {
charEnd = segment.getOffset();
}
void ParsedNumber::postProcess() {
if (!quantity.bogus && 0 != (flags & FLAG_NEGATIVE)) {
quantity.negate();
}
if (!quantity.bogus && 0 != (flags & FLAG_PERCENT)) {
quantity.adjustMagnitude(-2);
}
if (!quantity.bogus && 0 != (flags & FLAG_PERMILLE)) {
quantity.adjustMagnitude(-3);
}
}
bool ParsedNumber::success() const {
return charEnd > 0 && 0 == (flags & FLAG_FAIL);
}
@ -46,7 +58,6 @@ bool ParsedNumber::seenNumber() const {
}
double ParsedNumber::getDouble() const {
bool sawNegative = 0 != (flags & FLAG_NEGATIVE);
bool sawNaN = 0 != (flags & FLAG_NAN);
bool sawInfinity = 0 != (flags & FLAG_INFINITY);
@ -55,34 +66,25 @@ double ParsedNumber::getDouble() const {
return NAN;
}
if (sawInfinity) {
if (sawNegative) {
if (0 != (flags & FLAG_NEGATIVE)) {
return -INFINITY;
} else {
return INFINITY;
}
}
if (quantity.isZero() && sawNegative) {
U_ASSERT(!quantity.bogus);
if (quantity.isZero() && quantity.isNegative()) {
return -0.0;
}
if (quantity.fitsInLong()) {
long l = quantity.toLong();
if (0 != (flags & FLAG_NEGATIVE)) {
l *= -1;
}
return l;
return quantity.toLong();
} else {
return quantity.toDouble();
}
// TODO: MIN_LONG. It is supported in quantity.toLong() if quantity had the negative flag.
double d = quantity.toDouble();
if (0 != (flags & FLAG_NEGATIVE)) {
d *= -1;
}
return d;
}
void ParsedNumber::populateFormattable(Formattable& output) const {
bool sawNegative = 0 != (flags & FLAG_NEGATIVE);
bool sawNaN = 0 != (flags & FLAG_NAN);
bool sawInfinity = 0 != (flags & FLAG_INFINITY);
@ -92,7 +94,7 @@ void ParsedNumber::populateFormattable(Formattable& output) const {
return;
}
if (sawInfinity) {
if (sawNegative) {
if (0 != (flags & FLAG_NEGATIVE)) {
output.setDouble(-INFINITY);
return;
} else {
@ -100,17 +102,14 @@ void ParsedNumber::populateFormattable(Formattable& output) const {
return;
}
}
if (quantity.isZero() && sawNegative) {
U_ASSERT(!quantity.bogus);
if (quantity.isZero() && quantity.isNegative()) {
output.setDouble(-0.0);
return;
}
// All other numbers
LocalPointer<DecimalQuantity> actualQuantity(new DecimalQuantity(quantity));
if (0 != (flags & FLAG_NEGATIVE)) {
actualQuantity->negate();
}
output.adoptDecimalQuantity(actualQuantity.orphan());
output.adoptDecimalQuantity(new DecimalQuantity(quantity));
}
bool ParsedNumber::isBetterThan(const ParsedNumber& other) {

View file

@ -18,9 +18,36 @@ using namespace icu::numparse;
using namespace icu::numparse::impl;
namespace {
inline const UnicodeSet& minusSignSet() {
return *unisets::get(unisets::MINUS_SIGN);
}
inline const UnicodeSet& plusSignSet() {
return *unisets::get(unisets::PLUS_SIGN);
}
} // namespace
ScientificMatcher::ScientificMatcher(const DecimalFormatSymbols& dfs, const Grouper& grouper)
: fExponentSeparatorString(dfs.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol)),
fExponentMatcher(dfs, grouper, PARSE_FLAG_INTEGER_ONLY) {
fExponentMatcher(dfs, grouper, PARSE_FLAG_INTEGER_ONLY | PARSE_FLAG_GROUPING_DISABLED) {
const UnicodeString& minusSign = dfs.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
if (minusSignSet().contains(minusSign)) {
fCustomMinusSign.setToBogus();
} else {
fCustomMinusSign = minusSign;
}
const UnicodeString& plusSign = dfs.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol);
if (plusSignSet().contains(plusSign)) {
fCustomPlusSign.setToBogus();
} else {
fCustomPlusSign = plusSign;
}
}
bool ScientificMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const {
@ -37,18 +64,35 @@ bool ScientificMatcher::match(StringSegment& segment, ParsedNumber& result, UErr
// Full exponent separator match.
// First attempt to get a code point, returning true if we can't get one.
segment.adjustOffset(overlap1);
if (segment.length() == 0) {
if (segment.length() == overlap1) {
return true;
}
segment.adjustOffset(overlap1);
// Allow a sign, and then try to match digits.
int8_t exponentSign = 1;
if (segment.startsWith(*unisets::get(unisets::MINUS_SIGN))) {
if (segment.startsWith(minusSignSet())) {
exponentSign = -1;
segment.adjustOffsetByCodePoint();
} else if (segment.startsWith(*unisets::get(unisets::PLUS_SIGN))) {
} else if (segment.startsWith(plusSignSet())) {
segment.adjustOffsetByCodePoint();
} else if (segment.startsWith(fCustomMinusSign)) {
int32_t overlap2 = segment.getCommonPrefixLength(fCustomMinusSign);
if (overlap2 != fCustomMinusSign.length()) {
// Partial custom sign match; un-match the exponent separator.
segment.adjustOffset(-overlap1);
return true;
}
exponentSign = -1;
segment.adjustOffset(overlap2);
} else if (segment.startsWith(fCustomPlusSign)) {
int32_t overlap2 = segment.getCommonPrefixLength(fCustomPlusSign);
if (overlap2 != fCustomPlusSign.length()) {
// Partial custom sign match; un-match the exponent separator.
segment.adjustOffset(-overlap1);
return true;
}
segment.adjustOffset(overlap2);
}
int digitsOffset = segment.getOffset();

View file

@ -32,6 +32,8 @@ class ScientificMatcher : public NumberParseMatcher, public UMemory {
private:
UnicodeString fExponentSeparatorString;
DecimalMatcher fExponentMatcher;
UnicodeString fCustomMinusSign;
UnicodeString fCustomPlusSign;
};

View file

@ -152,13 +152,6 @@ PercentMatcher::PercentMatcher(const DecimalFormatSymbols& dfs)
: SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPercentSymbol), unisets::PERCENT_SIGN) {
}
void PercentMatcher::postProcess(ParsedNumber& result) const {
SymbolMatcher::postProcess(result);
if (0 != (result.flags & FLAG_PERCENT) && !result.quantity.bogus) {
result.quantity.adjustMagnitude(-2);
}
}
bool PercentMatcher::isDisabled(const ParsedNumber& result) const {
return 0 != (result.flags & FLAG_PERCENT);
}
@ -173,13 +166,6 @@ PermilleMatcher::PermilleMatcher(const DecimalFormatSymbols& dfs)
: SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol), unisets::PERMILLE_SIGN) {
}
void PermilleMatcher::postProcess(ParsedNumber& result) const {
SymbolMatcher::postProcess(result);
if (0 != (result.flags & FLAG_PERMILLE) && !result.quantity.bogus) {
result.quantity.adjustMagnitude(-3);
}
}
bool PermilleMatcher::isDisabled(const ParsedNumber& result) const {
return 0 != (result.flags & FLAG_PERMILLE);
}

View file

@ -124,8 +124,6 @@ class PercentMatcher : public SymbolMatcher {
PercentMatcher(const DecimalFormatSymbols& dfs);
void postProcess(ParsedNumber& result) const override;
protected:
bool isDisabled(const ParsedNumber& result) const override;
@ -139,8 +137,6 @@ class PermilleMatcher : public SymbolMatcher {
PermilleMatcher(const DecimalFormatSymbols& dfs);
void postProcess(ParsedNumber& result) const override;
protected:
bool isDisabled(const ParsedNumber& result) const override;

View file

@ -146,6 +146,9 @@ class ParsedNumber {
*/
void setCharsConsumed(const StringSegment& segment);
/** Apply certain number-related flags to the DecimalQuantity. */
void postProcess();
/**
* Returns whether this the parse was successful. To be successful, at least one char must have been
* consumed, and the failure flag must not be set.

View file

@ -1688,13 +1688,13 @@ void NumberFormatTest::TestSecondaryGrouping(void) {
CHECK(status, "DecimalFormat ct");
expect2(f, (int32_t)123456789L, "12,34,56,789");
expectPat(f, "#,##,###");
expectPat(f, "#,##,##0");
f.applyPattern("#,###", status);
CHECK(status, "applyPattern");
f.setSecondaryGroupingSize(4);
expect2(f, (int32_t)123456789L, "12,3456,789");
expectPat(f, "#,####,###");
expectPat(f, "#,####,##0");
NumberFormat *g = NumberFormat::createInstance(Locale("hi", "IN"), status);
CHECK_DATA(status, "createInstance(hi_IN)");
@ -1816,7 +1816,7 @@ void NumberFormatTest::TestScientific(void) {
int32_t PAT_length = UPRV_LENGTHOF(PAT);
int32_t DIGITS[] = {
// min int, max int, min frac, max frac
0, 1, 0, 0, // "#E0"
1, 1, 0, 0, // "#E0"
1, 1, 0, 4, // "0.####E0"
2, 2, 3, 3, // "00.000E00"
1, 3, 0, 4, // "##0.####E000"
@ -2159,7 +2159,7 @@ void NumberFormatTest::TestPatterns2(void) {
fmt.setFormatWidth(16);
// 12 34567890123456
expectPat(fmt, "AA*^#,###,##0.00ZZ");
expectPat(fmt, "AA*^#####,##0.00ZZ");
}
void NumberFormatTest::TestSurrogateSupport(void) {
@ -2223,9 +2223,9 @@ void NumberFormatTest::TestSurrogateSupport(void) {
int32_t(-20), expStr, status);
custom.setSymbol(DecimalFormatSymbols::kPercentSymbol, "percent");
patternStr = "'You''ve lost ' -0.00 %' of your money today'";
patternStr = "'You''ve lost ' 0.00 %' of your money today'";
patternStr = patternStr.unescape();
expStr = UnicodeString(" minus You've lost minus 2000decimal00 percent of your money today", "");
expStr = UnicodeString(" minus You've lost 2000decimal00 percent of your money today", "");
status = U_ZERO_ERROR;
expect2(new DecimalFormat(patternStr, custom, status),
int32_t(-20), expStr, status);

View file

@ -1601,6 +1601,15 @@ lenient parse output breaks
0 0 fail JK
0 +0 0 JK
test parse with scientific-separator-affix overlap
set locale en
begin
pattern lenient parse output breaks
0E0','x 1 5E3,x 5000
0E0','x 0 5E3,x 5000
0E0'.'x 1 5E3.x 5000
0E0'.'x 0 5E3.x 5000

View file

@ -86,6 +86,9 @@ public interface DecimalQuantity extends PluralRules.IFixedDecimal {
*/
public void multiplyBy(BigDecimal multiplicand);
/** Flips the sign from positive to negative and back. */
void negate();
/**
* Scales the number by a power of ten. For example, if the value is currently "1234.56", calling
* this method with delta=-3 will change the value to "1.23456".

View file

@ -199,6 +199,11 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
setToBigDecimal(temp);
}
@Override
public void negate() {
flags ^= NEGATIVE_FLAG;
}
@Override
public int getMagnitude() throws ArithmeticException {
if (precision == 0) {
@ -573,6 +578,9 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
for (int magnitude = scale + precision - 1; magnitude >= 0; magnitude--) {
result = result * 10 + getDigitPos(magnitude - scale);
}
if (isNegative()) {
result = -result;
}
return result;
}
@ -676,8 +684,9 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
}
result /= DOUBLE_MULTIPLIERS[-i];
}
if (isNegative())
if (isNegative()) {
result = -result;
}
return result;
}
@ -704,8 +713,9 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
result /= 1e22;
result /= DOUBLE_MULTIPLIERS[-delta];
}
if (isNegative())
result *= -1;
if (isNegative()) {
result = -result;
}
return result;
}

View file

@ -428,6 +428,9 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
public String toNumberString() {
StringBuilder sb = new StringBuilder();
if (usingBytes) {
if (precision == 0) {
sb.append('0');
}
for (int i = precision - 1; i >= 0; i--) {
sb.append(bcdBytes[i]);
}

View file

@ -129,7 +129,7 @@ public class AffixMatcher implements NumberParseMatcher {
continue;
}
// Flags for setting in the ParsedNumber
// Flags for setting in the ParsedNumber; the token matchers may add more.
int flags = (signum == -1) ? ParsedNumber.FLAG_NEGATIVE : 0;
// Note: it is indeed possible for posPrefix and posSuffix to both be null.
@ -223,6 +223,12 @@ public class AffixMatcher implements NumberParseMatcher {
result.suffix = "";
}
result.flags |= flags;
if (prefix != null) {
prefix.postProcess(result);
}
if (suffix != null) {
suffix.postProcess(result);
}
}
}

View file

@ -302,6 +302,7 @@ public class NumberParserImpl {
for (NumberParseMatcher matcher : matchers) {
matcher.postProcess(result);
}
result.postProcess();
}
private void parseGreedyRecursive(StringSegment segment, ParsedNumber result) {

View file

@ -2,7 +2,6 @@
// License & terms of use: http://www.unicode.org/copyright.html#License
package com.ibm.icu.impl.number.parse;
import java.math.BigDecimal;
import java.util.Comparator;
import com.ibm.icu.impl.StringSegment;
@ -112,6 +111,19 @@ public class ParsedNumber {
charEnd = segment.getOffset();
}
/** Apply certain number-related flags to the DecimalQuantity. */
public void postProcess() {
if (quantity != null && 0 != (flags & FLAG_NEGATIVE)) {
quantity.negate();
}
if (quantity != null && 0 != (flags & FLAG_PERCENT)) {
quantity.adjustMagnitude(-2);
}
if (quantity != null && 0 != (flags & FLAG_PERMILLE)) {
quantity.adjustMagnitude(-3);
}
}
/**
* Returns whether this the parse was successful. To be successful, at least one char must have been
* consumed, and the failure flag must not be set.
@ -129,7 +141,6 @@ public class ParsedNumber {
}
public Number getNumber(boolean forceBigDecimal) {
boolean sawNegative = 0 != (flags & FLAG_NEGATIVE);
boolean sawNaN = 0 != (flags & FLAG_NAN);
boolean sawInfinity = 0 != (flags & FLAG_INFINITY);
@ -138,35 +149,23 @@ public class ParsedNumber {
return Double.NaN;
}
if (sawInfinity) {
if (sawNegative) {
if (0 != (flags & FLAG_NEGATIVE)) {
return Double.NEGATIVE_INFINITY;
} else {
return Double.POSITIVE_INFINITY;
}
}
if (quantity.isZero() && sawNegative) {
assert quantity != null;
if (quantity.isZero() && quantity.isNegative()) {
return -0.0;
}
if (quantity.fitsInLong() && !forceBigDecimal) {
long l = quantity.toLong();
if (0 != (flags & FLAG_NEGATIVE)) {
l *= -1;
}
return l;
return quantity.toLong();
} else {
return quantity.toBigDecimal();
}
BigDecimal d = quantity.toBigDecimal();
if (0 != (flags & FLAG_NEGATIVE)) {
d = d.negate();
}
// Special case: MIN_LONG
// TODO: It is supported in quantity.toLong() if quantity had the negative flag.
if (d.compareTo(BigDecimal.valueOf(Long.MIN_VALUE)) == 0 && !forceBigDecimal) {
return Long.MIN_VALUE;
}
return d;
}
boolean isBetterThan(ParsedNumber other) {

View file

@ -41,14 +41,6 @@ public class PercentMatcher extends SymbolMatcher {
result.setCharsConsumed(segment);
}
@Override
public void postProcess(ParsedNumber result) {
super.postProcess(result);
if (0 != (result.flags & ParsedNumber.FLAG_PERCENT) && result.quantity != null) {
result.quantity.adjustMagnitude(-2);
}
}
@Override
public String toString() {
return "<PercentMatcher>";

View file

@ -41,14 +41,6 @@ public class PermilleMatcher extends SymbolMatcher {
result.setCharsConsumed(segment);
}
@Override
public void postProcess(ParsedNumber result) {
super.postProcess(result);
if (0 != (result.flags & ParsedNumber.FLAG_PERMILLE) && result.quantity != null) {
result.quantity.adjustMagnitude(-3);
}
}
@Override
public String toString() {
return "<PermilleMatcher>";

View file

@ -5,6 +5,7 @@ package com.ibm.icu.impl.number.parse;
import com.ibm.icu.impl.StringSegment;
import com.ibm.icu.impl.number.Grouper;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.UnicodeSet;
/**
* @author sffc
@ -14,6 +15,8 @@ public class ScientificMatcher implements NumberParseMatcher {
private final String exponentSeparatorString;
private final DecimalMatcher exponentMatcher;
private final String customMinusSign;
private final String customPlusSign;
public static ScientificMatcher getInstance(DecimalFormatSymbols symbols, Grouper grouper) {
// TODO: Static-initialize most common instances?
@ -24,7 +27,20 @@ public class ScientificMatcher implements NumberParseMatcher {
exponentSeparatorString = symbols.getExponentSeparator();
exponentMatcher = DecimalMatcher.getInstance(symbols,
grouper,
ParsingUtils.PARSE_FLAG_INTEGER_ONLY);
ParsingUtils.PARSE_FLAG_INTEGER_ONLY | ParsingUtils.PARSE_FLAG_GROUPING_DISABLED);
String minusSign = symbols.getMinusSignString();
customMinusSign = minusSignSet().contains(minusSign) ? null : minusSign;
String plusSign = symbols.getPlusSignString();
customPlusSign = plusSignSet().contains(plusSign) ? null : plusSign;
}
private static UnicodeSet minusSignSet() {
return UnicodeSetStaticCache.get(UnicodeSetStaticCache.Key.MINUS_SIGN);
}
private static UnicodeSet plusSignSet() {
return UnicodeSetStaticCache.get(UnicodeSetStaticCache.Key.PLUS_SIGN);
}
@Override
@ -42,18 +58,35 @@ public class ScientificMatcher implements NumberParseMatcher {
// Full exponent separator match.
// First attempt to get a code point, returning true if we can't get one.
segment.adjustOffset(overlap1);
if (segment.length() == 0) {
if (segment.length() == overlap1) {
return true;
}
segment.adjustOffset(overlap1);
// Allow a sign, and then try to match digits.
int exponentSign = 1;
if (segment.startsWith(UnicodeSetStaticCache.get(UnicodeSetStaticCache.Key.MINUS_SIGN))) {
if (segment.startsWith(minusSignSet())) {
exponentSign = -1;
segment.adjustOffsetByCodePoint();
} else if (segment.startsWith(UnicodeSetStaticCache.get(UnicodeSetStaticCache.Key.PLUS_SIGN))) {
} else if (segment.startsWith(plusSignSet())) {
segment.adjustOffsetByCodePoint();
} else if (segment.startsWith(customMinusSign)) {
int overlap2 = segment.getCommonPrefixLength(customMinusSign);
if (overlap2 != customMinusSign.length()) {
// Partial custom sign match; un-match the exponent separator.
segment.adjustOffset(-overlap1);
return true;
}
exponentSign = -1;
segment.adjustOffset(overlap2);
} else if (segment.startsWith(customPlusSign)) {
int overlap2 = segment.getCommonPrefixLength(customPlusSign);
if (overlap2 != customPlusSign.length()) {
// Partial custom sign match; un-match the exponent separator.
segment.adjustOffset(-overlap1);
return true;
}
segment.adjustOffset(overlap2);
}
int digitsOffset = segment.getOffset();

View file

@ -429,6 +429,11 @@ public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
}
}
@Override
public void negate() {
flags ^= NEGATIVE_FLAG;
}
/**
* Divide the internal number by the specified quotient. This method forces the internal
* representation into a BigDecimal. If you are dividing by a power of 10, use {@link

View file

@ -5955,4 +5955,23 @@ public class NumberFormatTest extends TestFmwk {
result.doubleValue(),
0.0);
}
@Test
public void testScientificCustomSign() {
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
dfs.setMinusSignString("nnn");
dfs.setPlusSignString("ppp");
DecimalFormat df = new DecimalFormat("0E0", dfs);
df.setExponentSignAlwaysShown(true);
expect2(df, 0.5, "5Ennn1");
expect2(df, 50, "5Eppp1");
}
@Test
public void testParsePercentInPattern() {
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
DecimalFormat df = new DecimalFormat("0x%", dfs);
df.setParseStrict(true);
expect2(df, 0.5, "50x%");
}
}