int length = process(inputDeque, modDeque, sb, 0);
// Resolve remaining affixes
- length += modDeque.applyAll(sb, 0, length);
+ modDeque.applyAll(sb, 0, length);
return sb.toString();
}
ModifierHolder mods = threadLocalModifierHolder.get().clear();
NumberStringBuilder sb = threadLocalStringBuilder.get().clear();
int length = process(input, mods, sb, 0);
- length += mods.applyAll(sb, 0, length);
+ mods.applyAll(sb, 0, length);
return sb.toString();
}
*/
public int getLowerDisplayMagnitude();
- public FormatQuantity clone();
+ /**
+ * Like clone, but without the restrictions of the Cloneable interface clone.
+ *
+ * @return A copy of this instance which can be mutated without affecting this instance.
+ */
+ public FormatQuantity createCopy();
public void copyFrom(FormatQuantity other);
}
@Override
- public FormatQuantity clone() {
+ public FormatQuantity1 createCopy() {
return new FormatQuantity1(this);
}
private int fractionCount() {
// TODO: This is temporary.
- FormatQuantity1 copy = (FormatQuantity1) this.clone();
+ FormatQuantity1 copy = new FormatQuantity1(this);
int fractionCount = 0;
while (copy.hasNextFraction()) {
copy.nextFraction();
@Override
public byte getDigit(int magnitude) {
// TODO: This is temporary.
- FormatQuantity1 copy = (FormatQuantity1) this.clone();
+ FormatQuantity1 copy = new FormatQuantity1(this);
if (magnitude < 0) {
for (int p = -1; p > magnitude; p--) {
copy.nextFraction();
private long bcdLong = 0L;
- private boolean usingBytes = false;;
+ private boolean usingBytes = false;
@Override
public int maxRepresentableDigits() {
}
private void ensureCapacity(int capacity) {
- if (bcdBytes == null && capacity > 0) {
+ if (capacity == 0) return;
+ if (bcdBytes == null) {
bcdBytes = new byte[capacity];
} else if (bcdBytes.length < capacity) {
byte[] bcd1 = new byte[capacity * 2];
protected static final int INFINITY_FLAG = 2;
protected static final int NAN_FLAG = 4;
+ // The following three fields relate to the double-to-ascii fast path algorithm.
+ // When a double is given to FormatQuantityBCD, it is converted to using a fast algorithm. The
+ // fast algorithm guarantees correctness to only the first ~12 digits of the double. The process
+ // of rounding the number ensures that the converted digits are correct, falling back to a slow-
+ // path algorithm if required. Therefore, if a FormatQuantity is constructed from a double, it
+ // is *required* that roundToMagnitude(), roundToIncrement(), or roundToInfinity() is called. If
+ // you don't round, assertions will fail in certain other methods if you try calling them.
+
/**
* The original number provided by the user and which is represented in BCD. Used when we need to
* re-compute the BCD for an exact double representation.
*/
protected double origDouble;
+ /**
+ * The change in magnitude relative to the original double. Used when we need to re-compute the
+ * BCD for an exact double representation.
+ */
protected int origDelta;
+
+ /**
+ * Whether the value in the BCD comes from the double fast path without having been rounded to
+ * ensure correctness
+ */
protected boolean isApproximate;
// Four positions: left optional '(', left required '[', right required ']', right optional ')'.
@Override
public double getPluralOperand(Operand operand) {
+ // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
+ // See the comment at the top of this file explaining the "isApproximate" field.
+ assert !isApproximate;
+
switch (operand) {
case i:
return toLong();
@Override
public int getUpperDisplayMagnitude() {
+ // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
+ // See the comment at the top of this file explaining the "isApproximate" field.
+ assert !isApproximate;
+
int magnitude = scale + precision;
int result = (lReqPos > magnitude) ? lReqPos : (lOptPos < magnitude) ? lOptPos : magnitude;
return result - 1;
@Override
public int getLowerDisplayMagnitude() {
+ // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
+ // See the comment at the top of this file explaining the "isApproximate" field.
+ assert !isApproximate;
+
int magnitude = scale;
int result = (rReqPos < magnitude) ? rReqPos : (rOptPos > magnitude) ? rOptPos : magnitude;
return result;
@Override
public byte getDigit(int magnitude) {
+ // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
+ // See the comment at the top of this file explaining the "isApproximate" field.
+ assert !isApproximate;
+
return getDigitPos(magnitude - scale);
}
}
@Override
- public FormatQuantity clone() {
+ public FormatQuantity createCopy() {
if (this instanceof FormatQuantity2) {
return new FormatQuantity2((FormatQuantity2) this);
} else if (this instanceof FormatQuantity3) {
} else if (this instanceof FormatQuantity4) {
return new FormatQuantity4((FormatQuantity4) this);
} else {
- throw new IllegalArgumentException("Don't know how to clone " + this.getClass());
+ throw new IllegalArgumentException("Don't know how to copy " + this.getClass());
}
}
flags |= INFINITY_FLAG;
} else if (n != 0) {
_setToDoubleFast(n);
-
- // TODO: Remove this when finished testing.
- // isApproximate = true;
- // origDouble = n;
- // origDelta = 0;
- // convertToAccurateDouble();
-
compact();
}
}
length = 0;
}
+ public NumberStringBuilder(NumberStringBuilder source) {
+ this(source.chars.length);
+ zero = source.zero;
+ length = source.length;
+ System.arraycopy(source.chars, zero, chars, zero, length);
+ System.arraycopy(source.fields, zero, fields, zero, length);
+ }
+
@Override
public int length() {
return length;
* NumberStringBuilder}.
*/
public int insert(int index, NumberStringBuilder other) {
- assert this != other;
+ if (this == other) {
+ throw new IllegalArgumentException("Cannot call insert/append on myself");
+ }
int count = other.length;
if (count == 0) return 0; // nothing to insert
int position = prepareForInsert(index, count);
if (start < 0 || end > length || end < start) {
throw new IndexOutOfBoundsException();
}
- NumberStringBuilder other = this.clone();
+ NumberStringBuilder other = new NumberStringBuilder(this);
other.zero = zero + start;
other.length = end - start;
return other;
return as.getIterator();
}
- @Override
- public NumberStringBuilder clone() {
- NumberStringBuilder other = new NumberStringBuilder(chars.length);
- other.zero = zero;
- other.length = length;
- System.arraycopy(chars, zero, other.chars, zero, length);
- System.arraycopy(fields, zero, other.fields, zero, length);
- return other;
- }
-
public NumberStringBuilder clear() {
zero = chars.length / 2;
length = 0;
// TODO(sffc): Remove this field if it is not necessary.
@SuppressWarnings("unused")
SeparatorType groupingType2;
+
TextTrieMap<Byte> digitTrie;
Set<AffixHolder> affixHolders = new HashSet<AffixHolder>();
new ConcurrentHashMap<ULocale, CurrencyAffixPatterns>();
static void addToState(ULocale uloc, ParserState state) {
- if (!currencyAffixPatterns.containsKey(uloc)) {
+ CurrencyAffixPatterns value = currencyAffixPatterns.get(uloc);
+ if (value == null) {
// There can be multiple threads computing the same CurrencyAffixPatterns simultaneously,
// but that scenario is harmless.
- CurrencyAffixPatterns value = new CurrencyAffixPatterns(uloc);
- currencyAffixPatterns.put(uloc, value);
+ CurrencyAffixPatterns newValue = new CurrencyAffixPatterns(uloc);
+ currencyAffixPatterns.putIfAbsent(uloc, newValue);
+ value = currencyAffixPatterns.get(uloc);
}
- CurrencyAffixPatterns instance = currencyAffixPatterns.get(uloc);
- state.affixHolders.addAll(instance.set);
+ state.affixHolders.addAll(value.set);
}
private CurrencyAffixPatterns(ULocale uloc) {
result.totalIntegerDigits += 1;
result.minimumIntegerDigits += 1;
// no change to result.minimumSignificantDigits
- result.maximumSignificantDigits += (seenSignificantDigitMarker ? 1 : 0);
+ // no change to result.maximumSignificantDigits
result.rounding.appendDigit((byte) (state.peek() - '0'), 0, true);
break;
* @return The multiplier that was chosen to best fit the input.
*/
public int chooseMultiplierAndApply(FormatQuantity input, MultiplierGenerator mg) {
- FormatQuantity copy = input.clone();
+ // TODO: Avoid the object creation here.
+ FormatQuantity copy = input.createCopy();
int magnitude = input.getMagnitude();
int multiplier = mg.getMultiplier(magnitude);
+++ /dev/null
-// © 2017 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-package com.ibm.icu.impl.number;
-
-import java.math.BigDecimal;
-import java.text.ParseException;
-import java.text.ParsePosition;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition;
-import com.ibm.icu.impl.number.formatters.RangeFormat;
-import com.ibm.icu.impl.number.modifiers.SimpleModifier;
-import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
-import com.ibm.icu.text.DecimalFormatSymbols;
-import com.ibm.icu.util.MeasureUnit;
-
-public class demo {
-
- public static void main(String[] args) throws ParseException {
- SimpleModifier.testFormatAsPrefixSuffix();
-
- System.out.println(new FormatQuantity1(3.14159));
- System.out.println(new FormatQuantity1(3.14159, true));
- System.out.println(new FormatQuantity2(3.14159));
-
- System.out.println(
- PatternString.propertiesToString(PatternString.parseToProperties("+**##,##,#00.05#%")));
-
- ParsePosition ppos = new ParsePosition(0);
- System.out.println(
- Parse.parse(
- "dd123",
- ppos,
- new Properties().setPositivePrefix("dd").setNegativePrefix("ddd"),
- DecimalFormatSymbols.getInstance()));
- System.out.println(ppos);
-
- List<Format> formats = new ArrayList<Format>();
-
- Properties properties = new Properties();
- Format ndf = Endpoint.fromBTA(properties);
- formats.add(ndf);
-
- properties =
- new Properties()
- .setMinimumSignificantDigits(3)
- .setMaximumSignificantDigits(3)
- .setCompactStyle(CompactStyle.LONG);
- Format cdf = Endpoint.fromBTA(properties);
- formats.add(cdf);
-
- properties =
- new Properties().setFormatWidth(10).setPadPosition(PadPosition.AFTER_PREFIX);
- Format pdf = Endpoint.fromBTA(properties);
- formats.add(pdf);
-
- properties =
- new Properties()
- .setMinimumExponentDigits(1)
- .setMaximumIntegerDigits(3)
- .setMaximumFractionDigits(1);
- Format exf = Endpoint.fromBTA(properties);
- formats.add(exf);
-
- properties = new Properties().setRoundingIncrement(new BigDecimal("0.5"));
- Format rif = Endpoint.fromBTA(properties);
- formats.add(rif);
-
- properties = new Properties().setMeasureUnit(MeasureUnit.HECTARE);
- Format muf = Endpoint.fromBTA(properties);
- formats.add(muf);
-
- properties =
- new Properties().setMeasureUnit(MeasureUnit.HECTARE).setCompactStyle(CompactStyle.LONG);
- Format cmf = Endpoint.fromBTA(properties);
- formats.add(cmf);
-
- properties = PatternString.parseToProperties("#,##0.00 \u00a4");
- Format ptf = Endpoint.fromBTA(properties);
- formats.add(ptf);
-
- RangeFormat rf = new RangeFormat(cdf, cdf, " to ");
- System.out.println(rf.format(new FormatQuantity2(1234), new FormatQuantity2(2345)));
-
- String[] cases = {
- "1.0",
- "2.01",
- "1234.56",
- "3000.0",
- // "512.0000000000017",
- // "4096.000000000001",
- // "4096.000000000004",
- // "4096.000000000005",
- // "4096.000000000006",
- // "4096.000000000007",
- "0.00026418",
- "0.01789261",
- "468160.0",
- "999000.0",
- "999900.0",
- "999990.0",
- "0.0",
- "12345678901.0",
- // "789000000000000000000000.0",
- // "789123123567853156372158.0",
- "-5193.48",
- };
-
- for (String str : cases) {
- System.out.println("----------");
- System.out.println(str);
- System.out.println(" NDF: " + ndf.format(new FormatQuantity2(Double.parseDouble(str))));
- System.out.println(" CDF: " + cdf.format(new FormatQuantity2(Double.parseDouble(str))));
- System.out.println(" PWD: " + pdf.format(new FormatQuantity2(Double.parseDouble(str))));
- System.out.println(" EXF: " + exf.format(new FormatQuantity2(Double.parseDouble(str))));
- System.out.println(" RIF: " + rif.format(new FormatQuantity2(Double.parseDouble(str))));
- System.out.println(" MUF: " + muf.format(new FormatQuantity2(Double.parseDouble(str))));
- System.out.println(" CMF: " + cmf.format(new FormatQuantity2(Double.parseDouble(str))));
- System.out.println(" PTF: " + ptf.format(new FormatQuantity2(Double.parseDouble(str))));
- }
- }
-}
@Override
public boolean equals(Object _other) {
if (_other == null) return false;
- if (this == _other) return true;
CompactDecimalFingerprint other = (CompactDecimalFingerprint) _other;
+ if (this == other) return true;
if (compactStyle != other.compactStyle) return false;
if (compactType != other.compactType) return false;
if (currencySymbol != other.currencySymbol) {
*/
@Deprecated
public IProperties setCurrencyPluralInfo(CurrencyPluralInfo currencyPluralInfo);
-
- public IProperties clone();
}
public static interface IProperties
* @return The property bag, for chaining.
*/
public IProperties setMinimumExponentDigits(int minimumExponentDigits);
-
- @Override
- public IProperties clone();
}
public static boolean useScientificNotation(IProperties properties) {
import com.ibm.icu.impl.number.FormatQuantity;
import com.ibm.icu.impl.number.Properties;
import com.ibm.icu.impl.number.Rounder;
-import com.ibm.icu.impl.number.Rounder.IBasicRoundingProperties;
public class SignificantDigitsRounder extends Rounder {
ENSURE_MINIMUM_SIGNIFICANT
};
- public static interface IProperties extends IBasicRoundingProperties {
+ public static interface IProperties extends Rounder.IBasicRoundingProperties {
static int DEFAULT_MINIMUM_SIGNIFICANT_DIGITS = -1;
import java.text.ParsePosition;
+import com.ibm.icu.impl.number.FormatQuantity4;
+
//===================================================================
// NFSubstitution (abstract base class)
//===================================================================
* @param that The substitution to compare this one to
* @return true if the two substitutions are functionally equivalent
*/
- public boolean equals(Object that) {
+ @Override
+ public boolean equals(Object that) {
// compare class and all of the fields all substitutions have
// in common
if (that == null) {
}
return false;
}
-
- public int hashCode() {
+
+ @Override
+ public int hashCode() {
assert false : "hashCode not designed";
return 42;
}
* not be identical to the description it was created from, but
* it'll produce the same result.
*/
- public String toString() {
+ @Override
+ public String toString() {
// use tokenChar() to get the character at the beginning and
// end of the substitution token. In between them will go
// either the name of the rule set it uses, or the pattern of
* Returns "number" unchanged.
* @return "number"
*/
- public long transformNumber(long number) {
+ @Override
+ public long transformNumber(long number) {
return number;
}
* Returns "number" unchanged.
* @return "number"
*/
- public double transformNumber(double number) {
+ @Override
+ public double transformNumber(double number) {
return number;
}
* substitution.
* @return newRuleValue
*/
- public double composeRuleValue(double newRuleValue, double oldRuleValue) {
+ @Override
+ public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue;
}
* @param oldUpperBound The current upper bound.
* @return oldUpperBound
*/
- public double calcUpperBound(double oldUpperBound) {
+ @Override
+ public double calcUpperBound(double oldUpperBound) {
return oldUpperBound;
}
* The token character for a SameValueSubstitution is =.
* @return '='
*/
- char tokenChar() {
+ @Override
+ char tokenChar() {
return '=';
}
}
* @param radix The radix of the divisor.
* @param exponent The exponent of the divisor.
*/
- public void setDivisor(int radix, short exponent) {
+ @Override
+ public void setDivisor(int radix, short exponent) {
divisor = NFRule.power(radix, exponent);
if (divisor == 0) {
* @param that The other substitution
* @return true if the two substitutions are functionally equal
*/
- public boolean equals(Object that) {
+ @Override
+ public boolean equals(Object that) {
return super.equals(that) && divisor == ((MultiplierSubstitution) that).divisor;
}
-
+
//-----------------------------------------------------------------------
// formatting
//-----------------------------------------------------------------------
* @param number The number being formatted.
* @return "number" divided by the rule's divisor
*/
- public long transformNumber(long number) {
+ @Override
+ public long transformNumber(long number) {
return (long)Math.floor(number / divisor);
}
* @param number The number being formatted
* @return "number" divided by the rule's divisor
*/
- public double transformNumber(double number) {
+ @Override
+ public double transformNumber(double number) {
if (ruleSet == null) {
return number / divisor;
} else {
* substitution
* @return newRuleValue * divisor
*/
- public double composeRuleValue(double newRuleValue, double oldRuleValue) {
+ @Override
+ public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue * divisor;
}
* @param oldUpperBound Ignored.
* @return The rule's divisor.
*/
- public double calcUpperBound(double oldUpperBound) {
+ @Override
+ public double calcUpperBound(double oldUpperBound) {
return divisor;
}
* The token character for a multiplier substitution is <.
* @return '<'
*/
- char tokenChar() {
+ @Override
+ char tokenChar() {
return '<';
}
}
* @param radix The radix of the divisor.
* @param exponent The exponent of the divisor.
*/
- public void setDivisor(int radix, short exponent) {
+ @Override
+ public void setDivisor(int radix, short exponent) {
divisor = NFRule.power(radix, exponent);
if (divisor == 0) { // this will cause recursion
* @param that The other substitution
* @return true if the two substitutions are functionally equivalent
*/
- public boolean equals(Object that) {
+ @Override
+ public boolean equals(Object that) {
if (super.equals(that)) {
ModulusSubstitution that2 = (ModulusSubstitution)that;
return false;
}
}
-
+
//-----------------------------------------------------------------------
// formatting
//-----------------------------------------------------------------------
* into
* @param position The position of the rule text in toInsertInto
*/
- public void doSubstitution(long number, StringBuilder toInsertInto, int position, int recursionCount) {
+ @Override
+ public void doSubstitution(long number, StringBuilder toInsertInto, int position, int recursionCount) {
// if this isn't a >>> substitution, just use the inherited version
// of this function (which uses either a rule set or a DecimalFormat
// to format its substitution value)
* into
* @param position The position of the rule text in toInsertInto
*/
- public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
+ @Override
+ public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
// if this isn't a >>> substitution, just use the inherited version
// of this function (which uses either a rule set or a DecimalFormat
// to format its substitution value)
* @param number The number being formatted
* @return "number" mod divisor
*/
- public long transformNumber(long number) {
+ @Override
+ public long transformNumber(long number) {
return number % divisor;
}
* @param number The number being formatted
* @return "number" mod divisor
*/
- public double transformNumber(double number) {
+ @Override
+ public double transformNumber(double number) {
return Math.floor(number % divisor);
}
* @param baseValue The partial parse result prior to calling this
* routine.
*/
- public Number doParse(String text, ParsePosition parsePosition, double baseValue,
+ @Override
+ public Number doParse(String text, ParsePosition parsePosition, double baseValue,
double upperBound, boolean lenientParse) {
// if this isn't a >>> substitution, we can just use the
// inherited parse() routine to do the parsing
* @param oldRuleValue The base value of the rule containing the
* substitution
*/
- public double composeRuleValue(double newRuleValue, double oldRuleValue) {
+ @Override
+ public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue;
}
* @param oldUpperBound Ignored
* @return The owning rule's divisor
*/
- public double calcUpperBound(double oldUpperBound) {
+ @Override
+ public double calcUpperBound(double oldUpperBound) {
return divisor;
}
* Returns true. This _is_ a ModulusSubstitution.
* @return true
*/
- public boolean isModulusSubstitution() {
+ @Override
+ public boolean isModulusSubstitution() {
return true;
}
* The token character of a ModulusSubstitution is >.
* @return '>'
*/
- char tokenChar() {
+ @Override
+ char tokenChar() {
return '>';
}
}
* @param number The number being formatted
* @return "number" unchanged
*/
- public long transformNumber(long number) {
+ @Override
+ public long transformNumber(long number) {
return number;
}
* @param number The integral part of the number being formatted
* @return floor(number)
*/
- public double transformNumber(double number) {
+ @Override
+ public double transformNumber(double number) {
return Math.floor(number);
}
* calling this function
* @return oldRuleValue + newRuleValue
*/
- public double composeRuleValue(double newRuleValue, double oldRuleValue) {
+ @Override
+ public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue + oldRuleValue;
}
* @param oldUpperBound Ignored
* @return Double.MAX_VALUE
*/
- public double calcUpperBound(double oldUpperBound) {
+ @Override
+ public double calcUpperBound(double oldUpperBound) {
return Double.MAX_VALUE;
}
* An IntegralPartSubstitution's token character is <
* @return '<'
*/
- char tokenChar() {
+ @Override
+ char tokenChar() {
return '<';
}
}
* @param position The position of the owning rule's rule text in
* toInsertInto
*/
- public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
+ @Override
+ public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
if (!byDigits) {
// if we're not in "byDigits" mode, just use the inherited
// doSubstitution() routine
// (this is slower, but more accurate, than doing it from the
// other end)
- // just print to string and then use that
- DigitList dl = new DigitList();
- dl.set(number, 20, true);
+ FormatQuantity4 fq = new FormatQuantity4(number);
+ fq.roundToInfinity(); // ensure doubles are resolved using slow path
boolean pad = false;
- while (dl.count > Math.max(0, dl.decimalAt)) {
+ int mag = fq.getLowerDisplayMagnitude();
+ while (mag < 0) {
if (pad && useSpaces) {
toInsertInto.insert(position + pos, ' ');
} else {
pad = true;
}
- ruleSet.format(dl.digits[--dl.count] - '0', toInsertInto, position + pos, recursionCount);
- }
- while (dl.decimalAt < 0) {
- if (pad && useSpaces) {
- toInsertInto.insert(position + pos, ' ');
- } else {
- pad = true;
- }
- ruleSet.format(0, toInsertInto, position + pos, recursionCount);
- ++dl.decimalAt;
+ ruleSet.format(fq.getDigit(mag++), toInsertInto, position + pos, recursionCount);
}
}
}
* @param number The number being formatted
* @return 0
*/
- public long transformNumber(long number) {
+ @Override
+ public long transformNumber(long number) {
return 0;
}
* @param number The number being formatted.
* @return number - floor(number)
*/
- public double transformNumber(double number) {
+ @Override
+ public double transformNumber(double number) {
return number - Math.floor(number);
}
* result; otherwise new Long(0). The result is either a Long or
* a Double.
*/
- public Number doParse(String text, ParsePosition parsePosition, double baseValue,
+ @Override
+ public Number doParse(String text, ParsePosition parsePosition, double baseValue,
double upperBound, boolean lenientParse) {
// if we're not in byDigits mode, we can just use the inherited
// doParse()
double result;
int digit;
- DigitList dl = new DigitList();
+ FormatQuantity4 fq = new FormatQuantity4();
+ int leadingZeros = 0;
while (workText.length() > 0 && workPos.getIndex() != 0) {
workPos.setIndex(0);
digit = ruleSet.parse(workText, workPos, 10).intValue();
}
if (workPos.getIndex() != 0) {
- dl.append('0'+digit);
+ if (digit == 0) {
+ leadingZeros++;
+ } else {
+ fq.appendDigit((byte) digit, leadingZeros, false);
+ leadingZeros = 0;
+ }
parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
workText = workText.substring(workPos.getIndex());
}
}
}
- result = dl.count == 0 ? 0 : dl.getDouble();
+ result = fq.toDouble();
result = composeRuleValue(result, baseValue);
return new Double(result);
* this function
* @return newRuleValue + oldRuleValue
*/
- public double composeRuleValue(double newRuleValue, double oldRuleValue) {
+ @Override
+ public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue + oldRuleValue;
}
/**
* Not used.
*/
- public double calcUpperBound(double oldUpperBound) {
+ @Override
+ public double calcUpperBound(double oldUpperBound) {
return 0; // this value is ignored
}
* The token character for a FractionalPartSubstitution is >.
* @return '>'
*/
- char tokenChar() {
+ @Override
+ char tokenChar() {
return '>';
}
}
* @param number The number being formatted.
* @return abs(number)
*/
- public long transformNumber(long number) {
+ @Override
+ public long transformNumber(long number) {
return Math.abs(number);
}
* @param number The number being formatted.
* @return abs(number)
*/
- public double transformNumber(double number) {
+ @Override
+ public double transformNumber(double number) {
return Math.abs(number);
}
* this function
* @return -newRuleValue
*/
- public double composeRuleValue(double newRuleValue, double oldRuleValue) {
+ @Override
+ public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return -newRuleValue;
}
* @param oldUpperBound Ignored.
* @return Double.MAX_VALUE
*/
- public double calcUpperBound(double oldUpperBound) {
+ @Override
+ public double calcUpperBound(double oldUpperBound) {
return Double.MAX_VALUE;
}
* The token character for an AbsoluteValueSubstitution is >
* @return '>'
*/
- char tokenChar() {
+ @Override
+ char tokenChar() {
return '>';
}
}
// Rather than keeping a backpointer to the rule, we copy its
// base value here
this.denominator = denominator;
-
+
this.withZeros = description.endsWith("<<");
}
static String fixdesc(String description) {
- return description.endsWith("<<")
- ? description.substring(0,description.length()-1)
+ return description.endsWith("<<")
+ ? description.substring(0,description.length()-1)
: description;
}
* @param that The other NumeratorSubstitution
* @return true if the two objects are functionally equivalent
*/
- public boolean equals(Object that) {
+ @Override
+ public boolean equals(Object that) {
if (super.equals(that)) {
NumeratorSubstitution that2 = (NumeratorSubstitution)that;
return denominator == that2.denominator && withZeros == that2.withZeros;
return false;
}
}
-
+
//-----------------------------------------------------------------------
// formatting
//-----------------------------------------------------------------------
* rule text begins (this value is added to this substitution's
* position to determine exactly where to insert the new text)
*/
- public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
+ @Override
+ public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
// perform a transformation on the number being formatted that
// is dependent on the type of substitution this is
//String s = toInsertInto.toString();
* @param number The number being formatted
* @return number * denominator
*/
- public long transformNumber(long number) {
+ @Override
+ public long transformNumber(long number) {
return Math.round(number * denominator);
}
* @param number The number being formatted
* @return number * denominator
*/
- public double transformNumber(double number) {
+ @Override
+ public double transformNumber(double number) {
return Math.round(number * denominator);
}
* Dispatches to the inherited version of this function, but makes
* sure that lenientParse is off.
*/
- public Number doParse(String text, ParsePosition parsePosition, double baseValue,
+ @Override
+ public Number doParse(String text, ParsePosition parsePosition, double baseValue,
double upperBound, boolean lenientParse) {
// we don't have to do anything special to do the parsing here,
// but we have to turn lenient parsing off-- if we leave it on,
if (withZeros) {
// any base value will do in this case. is there a way to
// force this to not bother trying all the base values?
-
+
// compute the 'effective' base and prescale the value down
long n = result.longValue();
long d = 1;
* @param oldRuleValue The owning rule's base value
* @return newRuleValue / oldRuleValue
*/
- public double composeRuleValue(double newRuleValue, double oldRuleValue) {
+ @Override
+ public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue / oldRuleValue;
}
* @param oldUpperBound Ignored
* @return The base value of the rule owning this substitution
*/
- public double calcUpperBound(double oldUpperBound) {
+ @Override
+ public double calcUpperBound(double oldUpperBound) {
return denominator;
}
* The token character for a NumeratorSubstitution is <
* @return '<'
*/
- char tokenChar() {
+ @Override
+ char tokenChar() {
return '<';
}
}
private static void testFormatQuantityExpectedOutput(FormatQuantity rq, String expected) {
StringBuilder sb = new StringBuilder();
- FormatQuantity q0 = rq.clone();
+ FormatQuantity q0 = rq.createCopy();
// Force an accurate double
q0.roundToInfinity();
q0.setIntegerFractionLength(1, Integer.MAX_VALUE, 1, Integer.MAX_VALUE);
new MathContext(3, RoundingMode.HALF_UP);
private static void testFormatQuantityRounding(FormatQuantity rq0, FormatQuantity rq1) {
- FormatQuantity q0 = rq0.clone();
- FormatQuantity q1 = rq1.clone();
+ FormatQuantity q0 = rq0.createCopy();
+ FormatQuantity q1 = rq1.createCopy();
q0.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN);
q1.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN);
testFormatQuantityBehavior(q0, q1);
- q0 = rq0.clone();
- q1 = rq1.clone();
+ q0 = rq0.createCopy();
+ q1 = rq1.createCopy();
q0.roundToMagnitude(-1, MATH_CONTEXT_CEILING);
q1.roundToMagnitude(-1, MATH_CONTEXT_CEILING);
testFormatQuantityBehavior(q0, q1);
- q0 = rq0.clone();
- q1 = rq1.clone();
+ q0 = rq0.createCopy();
+ q1 = rq1.createCopy();
q0.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
q1.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
testFormatQuantityBehavior(q0, q1);
}
private static void testFormatQuantityRoundingInterval(FormatQuantity rq0, FormatQuantity rq1) {
- FormatQuantity q0 = rq0.clone();
- FormatQuantity q1 = rq1.clone();
+ FormatQuantity q0 = rq0.createCopy();
+ FormatQuantity q1 = rq1.createCopy();
q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN);
q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN);
testFormatQuantityBehavior(q0, q1);
- q0 = rq0.clone();
- q1 = rq1.clone();
+ q0 = rq0.createCopy();
+ q1 = rq1.createCopy();
q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING);
q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING);
testFormatQuantityBehavior(q0, q1);
}
private static void testFormatQuantityMath(FormatQuantity rq0, FormatQuantity rq1) {
- FormatQuantity q0 = rq0.clone();
- FormatQuantity q1 = rq1.clone();
+ FormatQuantity q0 = rq0.createCopy();
+ FormatQuantity q1 = rq1.createCopy();
q0.adjustMagnitude(-3);
q1.adjustMagnitude(-3);
testFormatQuantityBehavior(q0, q1);
- q0 = rq0.clone();
- q1 = rq1.clone();
+ q0 = rq0.createCopy();
+ q1 = rq1.createCopy();
q0.multiplyBy(new BigDecimal("3.14159"));
q1.multiplyBy(new BigDecimal("3.14159"));
testFormatQuantityBehavior(q0, q1);
private static void testFormatQuantityWithFormats(
FormatQuantity rq0, FormatQuantity rq1, List<Format> formats) {
for (Format format : formats) {
- FormatQuantity q0 = rq0.clone();
- FormatQuantity q1 = rq1.clone();
+ FormatQuantity q0 = rq0.createCopy();
+ FormatQuantity q1 = rq1.createCopy();
String s1 = format.format(q0);
String s2 = format.format(q1);
assertEquals("Different output from formatter (" + q0 + ", " + q1 + ")", s1, s2);
}
private static void testFormatQuantityBehavior(FormatQuantity rq0, FormatQuantity rq1) {
- FormatQuantity q0 = rq0.clone();
- FormatQuantity q1 = rq1.clone();
+ FormatQuantity q0 = rq0.createCopy();
+ FormatQuantity q1 = rq1.createCopy();
assertEquals("Different sign (" + q0 + ", " + q1 + ")", q0.isNegative(), q1.isNegative());
q0.getPositionFingerprint(),
q1.getPositionFingerprint());
- assertEquals(
- "Different upper range of digits (" + q0 + ", " + q1 + ")",
- q0.getUpperDisplayMagnitude(),
- q1.getUpperDisplayMagnitude());
-
assertDoubleEquals(
"Different double values (" + q0 + ", " + q1 + ")", q0.toDouble(), q1.toDouble());
q0.toBigDecimal(),
q1.toBigDecimal());
- int equalityDigits = Math.min(q0.maxRepresentableDigits(), q1.maxRepresentableDigits());
- for (int m = q0.getUpperDisplayMagnitude(), i = 0;
- m >= Math.min(q0.getLowerDisplayMagnitude(), q1.getLowerDisplayMagnitude())
- && i < equalityDigits;
- m--, i++) {
+ q0.roundToInfinity();
+ q1.roundToInfinity();
+
+ assertEquals(
+ "Different lower display magnitude",
+ q0.getLowerDisplayMagnitude(),
+ q1.getLowerDisplayMagnitude());
+ assertEquals(
+ "Different upper display magnitude",
+ q0.getUpperDisplayMagnitude(),
+ q1.getUpperDisplayMagnitude());
+
+ for (int m = q0.getUpperDisplayMagnitude(); m >= q0.getLowerDisplayMagnitude(); m--) {
assertEquals(
"Different digit at magnitude " + m + " (" + q0 + ", " + q1 + ")",
q0.getDigit(m),
assertBigDecimalEquals("Failed on append", expected.toString(), fq.toBigDecimal());
assertNull("Failed health check", fq.checkHealth());
}
+ fq.appendDigit((byte) 9, 2, false);
+ expected.append("009");
+ assertBigDecimalEquals("Failed on append", expected.toString(), fq.toBigDecimal());
+ assertNull("Failed health check", fq.checkHealth());
}
@Test
assertEquals(fields[2], NumberFormat.Field.INTEGER);
}
- sb.append(sb.clone());
+ sb.append(new NumberStringBuilder(sb));
sb.append(sb.toCharArray(), sb.toFieldArray());
int numNull = 0;
int numCurr = 0;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.math.MathContext;
+import com.ibm.icu.text.CurrencyPluralInfo;
+import com.ibm.icu.text.DecimalFormatSymbols;
+import com.ibm.icu.text.NumberFormat;
+import com.ibm.icu.text.PluralRules;
+import com.ibm.icu.text.UFieldPosition;
+import com.ibm.icu.text.UTF16;
+import com.ibm.icu.text.UnicodeSet;
+import com.ibm.icu.text.NumberFormat.Field;
import com.ibm.icu.text.PluralRules.FixedDecimal;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.Currency.CurrencyUsage;
import java.math.BigInteger;
+import com.ibm.icu.text.DecimalFormat;
+import com.ibm.icu.text.NumberFormat;
+
/**
* <code>DigitList</code> handles the transcoding between numeric values and
* strings of characters. It only represents non-negative numbers. The
* @version 1.18 08/12/98
* @author Mark Davis, Alan Liu
* */
-final class DigitList {
+public final class DigitList {
/**
* The maximum number of significant digits in an IEEE 754 double, that
* is, in a Java double. This must not be increased, or garbage digits
ensureCapacity(count+1, count);
digits[count++] = (byte) digit;
}
-
+
public byte getDigitValue(int i) {
return (byte) (digits[i] - '0');
}
-
+
/**
* Utility routine to get the value of the digit list
* If (count == 0) this throws a NumberFormatException, which
}
for (int i = n; i < text.length; ++i) {
text[i] = '0';
- }
+ }
return new BigInteger(new String(text));
}
}
long scale = (long)count - (long)decimalAt;
if (scale > 0) {
int numDigits = count;
- if (scale > (long)Integer.MAX_VALUE) {
+ if (scale > Integer.MAX_VALUE) {
// try to reduce the scale
- long numShift = scale - (long)Integer.MAX_VALUE;
+ long numShift = scale - Integer.MAX_VALUE;
if (numShift < count) {
numDigits -= numShift;
} else {
}
}
- // Value to indicate that rounding was done.
+ // Value to indicate that rounding was done.
private boolean didRound = false;
-
+
/**
* Indicates if last digit set was rounded or not.
* true indicates it was rounded.
public boolean wasRounded() {
return didRound;
}
-
+
/**
* Utility routine to set the value of the digit list from a long
*/
// be represented by DigitList.
// [NEW] Faster implementation
didRound = false;
-
+
if (source <= 0) {
if (source == Long.MIN_VALUE) {
decimalAt = count = MAX_LONG_DIGITS;
int left = MAX_LONG_DIGITS;
int right;
while (source > 0) {
- digits[--left] = (byte) (((long) '0') + (source % 10));
+ digits[--left] = (byte) (('0') + (source % 10));
source /= 10;
}
decimalAt = MAX_LONG_DIGITS-left;
for (right = MAX_LONG_DIGITS - 1; digits[right] == (byte) '0'; --right) {}
count = right - left + 1;
System.arraycopy(digits, left, digits, 0, count);
- }
+ }
if (maximumDigits > 0) round(maximumDigits);
}
count = decimalAt = stringDigits.length();
didRound = false;
-
+
// Don't copy trailing zeros
while (count > 1 && stringDigits.charAt(count - 1) == '0') --count;
/**
* equality test between two digit lists.
*/
- public boolean equals(Object obj) {
+ @Override
+ public boolean equals(Object obj) {
if (this == obj) // quick check
return true;
if (!(obj instanceof DigitList)) // (1) same object?
/**
* Generates the hash code for the digit list.
*/
- public int hashCode() {
+ @Override
+ public int hashCode() {
int hashcode = decimalAt;
for (int i = 0; i < count; i++)
return hashcode;
}
- public String toString()
+ @Override
+ public String toString()
{
if (isZero()) return "0";
StringBuilder buf = new StringBuilder("0.");