| // © 2017 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| package com.ibm.icu.dev.impl.number; |
| |
| import java.math.BigDecimal; |
| import java.math.MathContext; |
| import java.math.RoundingMode; |
| import java.text.FieldPosition; |
| |
| import com.ibm.icu.impl.StandardPlural; |
| import com.ibm.icu.impl.number.DecimalQuantity; |
| import com.ibm.icu.impl.number.Modifier.Signum; |
| import com.ibm.icu.text.PluralRules; |
| import com.ibm.icu.text.PluralRules.Operand; |
| import com.ibm.icu.text.UFieldPosition; |
| |
| /** |
| * This is an older implementation of DecimalQuantity. A newer, faster implementation is |
| * DecimalQuantity2. I kept this implementation around because it was useful for testing purposes |
| * (being able to compare the output of one implementation with the other). |
| * |
| * <p>This class is NOT IMMUTABLE and NOT THREAD SAFE and is intended to be used by a single thread |
| * to format a number through a formatter, which is thread-safe. |
| */ |
| public class DecimalQuantity_SimpleStorage implements DecimalQuantity { |
| // 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 | |
| // +---------+---------+---------+---------+------------+------------------------+--------------+ |
| // |
| private int lOptPos = Integer.MAX_VALUE; |
| private int lReqPos = 0; |
| private int rReqPos = 0; |
| private int rOptPos = Integer.MIN_VALUE; |
| |
| // Internally, attempt to use a long to store the number. A long can hold numbers between 18 and |
| // 19 digits, covering the vast majority of use cases. We store three values: the long itself, |
| // the "scale" of the long (the power of 10 represented by the rightmost digit in the long), and |
| // the "precision" (the number of digits in the long). "primary" and "primaryScale" are the only |
| // two variables that are required for representing the number in memory. "primaryPrecision" is |
| // saved only for the sake of performance enhancements when performing certain operations. It can |
| // always be re-computed from "primary" and "primaryScale". |
| private long primary; |
| private int primaryScale; |
| private int primaryPrecision; |
| |
| // If the decimal can't fit into the long, fall back to a BigDecimal. |
| private BigDecimal fallback; |
| |
| // Other properties |
| private int flags; |
| private static final int NEGATIVE_FLAG = 1; |
| private static final int INFINITY_FLAG = 2; |
| private static final int NAN_FLAG = 4; |
| private static final long[] POWERS_OF_TEN = { |
| 1L, |
| 10L, |
| 100L, |
| 1000L, |
| 10000L, |
| 100000L, |
| 1000000L, |
| 10000000L, |
| 100000000L, |
| 1000000000L, |
| 10000000000L, |
| 100000000000L, |
| 1000000000000L, |
| 10000000000000L, |
| 100000000000000L, |
| 1000000000000000L, |
| 10000000000000000L, |
| 100000000000000000L, |
| 1000000000000000000L |
| }; |
| |
| private int origPrimaryScale; |
| |
| @Override |
| public int maxRepresentableDigits() { |
| return Integer.MAX_VALUE; |
| } |
| |
| public DecimalQuantity_SimpleStorage(long input) { |
| if (input < 0) { |
| setNegative(true); |
| input *= -1; |
| } |
| |
| primary = input; |
| primaryScale = 0; |
| primaryPrecision = computePrecision(primary); |
| fallback = null; |
| origPrimaryScale = primaryScale; |
| } |
| |
| /** |
| * Creates a DecimalQuantity from the given double value. Internally attempts several strategies |
| * for converting the double to an exact representation, falling back on a BigDecimal if it fails |
| * to do so. |
| * |
| * @param input The double to represent by this DecimalQuantity. |
| */ |
| public DecimalQuantity_SimpleStorage(double input) { |
| if (input < 0) { |
| setNegative(true); |
| input *= -1; |
| } |
| |
| // First try reading from IEEE bits. This is trivial only for doubles in [2^52, 2^64). If it |
| // fails, we wasted only a few CPU cycles. |
| long ieeeBits = Double.doubleToLongBits(input); |
| int exponent = (int) ((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff; |
| if (exponent >= 52 && exponent <= 63) { |
| // We can convert this double directly to a long. |
| long mantissa = (ieeeBits & 0x000fffffffffffffL) + 0x0010000000000000L; |
| primary = (mantissa << (exponent - 52)); |
| primaryScale = 0; |
| primaryPrecision = computePrecision(primary); |
| return; |
| } |
| |
| // Now try parsing the string produced by Double.toString(). |
| String temp = Double.toString(input); |
| try { |
| if (temp.length() == 3 && temp.equals("0.0")) { |
| // Case 1: Zero. |
| primary = 0L; |
| primaryScale = 0; |
| primaryPrecision = 0; |
| } else if (temp.indexOf('E') != -1) { |
| // Case 2: Exponential notation. |
| assert temp.indexOf('.') == 1; |
| int expPos = temp.indexOf('E'); |
| primary = Long.parseLong(temp.charAt(0) + temp.substring(2, expPos)); |
| primaryScale = Integer.parseInt(temp.substring(expPos + 1)) - (expPos - 1) + 1; |
| primaryPrecision = expPos - 1; |
| } else if (temp.charAt(0) == '0') { |
| // Case 3: Fraction-only number. |
| assert temp.indexOf('.') == 1; |
| primary = Long.parseLong(temp.substring(2)); // ignores leading zeros |
| primaryScale = 2 - temp.length(); |
| primaryPrecision = computePrecision(primary); |
| } else if (temp.charAt(temp.length() - 1) == '0') { |
| // Case 4: Integer-only number. |
| assert temp.indexOf('.') == temp.length() - 2; |
| int rightmostNonzeroDigitIndex = temp.length() - 3; |
| while (temp.charAt(rightmostNonzeroDigitIndex) == '0') { |
| rightmostNonzeroDigitIndex -= 1; |
| } |
| primary = Long.parseLong(temp.substring(0, rightmostNonzeroDigitIndex + 1)); |
| primaryScale = temp.length() - rightmostNonzeroDigitIndex - 3; |
| primaryPrecision = rightmostNonzeroDigitIndex + 1; |
| } else if (temp.equals("Infinity")) { |
| // Case 5: Infinity. |
| primary = 0; |
| setInfinity(true); |
| } else if (temp.equals("NaN")) { |
| // Case 6: NaN. |
| primary = 0; |
| setNaN(true); |
| } else { |
| // Case 7: Number with both a fraction and an integer. |
| int decimalPos = temp.indexOf('.'); |
| primary = Long.parseLong(temp.substring(0, decimalPos) + temp.substring(decimalPos + 1)); |
| primaryScale = decimalPos - temp.length() + 1; |
| primaryPrecision = temp.length() - 1; |
| } |
| } catch (NumberFormatException e) { |
| // The digits of the double can't fit into the long. |
| primary = -1; |
| fallback = new BigDecimal(temp); |
| } |
| |
| origPrimaryScale = primaryScale; |
| } |
| |
| static final double LOG_2_OF_TEN = 3.32192809489; |
| |
| public DecimalQuantity_SimpleStorage(double input, boolean fast) { |
| if (input < 0) { |
| setNegative(true); |
| input *= -1; |
| } |
| |
| // Our strategy is to read all digits that are *guaranteed* to be valid without delving into |
| // the IEEE rounding rules. This strategy might not end up with a perfect representation of |
| // the fractional part of the double. |
| long ieeeBits = Double.doubleToLongBits(input); |
| int exponent = (int) ((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff; |
| long mantissa = (ieeeBits & 0x000fffffffffffffL) + 0x0010000000000000L; |
| if (exponent > 63) { |
| throw new IllegalArgumentException(); // FIXME |
| } else if (exponent >= 52) { |
| primary = (mantissa << (exponent - 52)); |
| primaryScale = 0; |
| primaryPrecision = computePrecision(primary); |
| return; |
| } else if (exponent >= 0) { |
| int shift = 52 - exponent; |
| primary = (mantissa >> shift); // integer part |
| int fractionCount = (int) (shift / LOG_2_OF_TEN); |
| long fraction = (mantissa - (primary << shift)) + 1L; // TODO: Explain the +1L |
| primary *= POWERS_OF_TEN[fractionCount]; |
| for (int i = 0; i < fractionCount; i++) { |
| long times10 = (fraction * 10L); |
| long digit = times10 >> shift; |
| assert digit >= 0 && digit < 10; |
| primary += digit * POWERS_OF_TEN[fractionCount - i - 1]; |
| fraction = times10 & ((1L << shift) - 1); |
| } |
| primaryScale = -fractionCount; |
| primaryPrecision = computePrecision(primary); |
| } else { |
| throw new IllegalArgumentException(); // FIXME |
| } |
| } |
| |
| public DecimalQuantity_SimpleStorage(BigDecimal decimal) { |
| setToBigDecimal(decimal); |
| } |
| |
| public DecimalQuantity_SimpleStorage(DecimalQuantity_SimpleStorage other) { |
| copyFrom(other); |
| } |
| |
| @Override |
| public void setToBigDecimal(BigDecimal decimal) { |
| if (decimal.compareTo(BigDecimal.ZERO) < 0) { |
| setNegative(true); |
| decimal = decimal.negate(); |
| } |
| |
| primary = -1; |
| if (decimal.compareTo(BigDecimal.ZERO) == 0) { |
| fallback = BigDecimal.ZERO; |
| } else { |
| fallback = decimal; |
| } |
| } |
| |
| @Override |
| public DecimalQuantity_SimpleStorage createCopy() { |
| return new DecimalQuantity_SimpleStorage(this); |
| } |
| |
| /** |
| * Make the internal state of this DecimalQuantity equal to another DecimalQuantity. |
| * |
| * @param other The template DecimalQuantity. All properties from this DecimalQuantity will be |
| * copied into this DecimalQuantity. |
| */ |
| @Override |
| public void copyFrom(DecimalQuantity other) { |
| // TODO: Check before casting |
| DecimalQuantity_SimpleStorage _other = (DecimalQuantity_SimpleStorage) other; |
| lOptPos = _other.lOptPos; |
| lReqPos = _other.lReqPos; |
| rReqPos = _other.rReqPos; |
| rOptPos = _other.rOptPos; |
| primary = _other.primary; |
| primaryScale = _other.primaryScale; |
| primaryPrecision = _other.primaryPrecision; |
| fallback = _other.fallback; |
| flags = _other.flags; |
| origPrimaryScale = _other.origPrimaryScale; |
| } |
| |
| @Override |
| public long getPositionFingerprint() { |
| long fingerprint = 0; |
| fingerprint ^= lOptPos; |
| fingerprint ^= (lReqPos << 16); |
| fingerprint ^= ((long) rReqPos << 32); |
| fingerprint ^= ((long) rOptPos << 48); |
| return fingerprint; |
| } |
| |
| /** |
| * Utility method to compute the number of digits ("precision") in a long. |
| * |
| * @param input The long (which can't contain more than 19 digits). |
| * @return The precision of the long. |
| */ |
| private static int computePrecision(long input) { |
| int precision = 0; |
| while (input > 0) { |
| input /= 10; |
| precision++; |
| } |
| return precision; |
| } |
| |
| /** |
| * Changes the internal representation from a long to a BigDecimal. Used only for operations that |
| * don't support longs. |
| */ |
| private void convertToBigDecimal() { |
| if (primary == -1) { |
| return; |
| } |
| |
| fallback = new BigDecimal(primary).scaleByPowerOfTen(primaryScale); |
| primary = -1; |
| } |
| |
| @Override |
| public void setMinInteger(int minInt) { |
| // Graceful failures for bogus input |
| minInt = Math.max(0, minInt); |
| |
| // Save values into internal state |
| // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE |
| lReqPos = minInt; |
| } |
| |
| @Override |
| public void setMinFraction(int minFrac) { |
| // Graceful failures for bogus input |
| minFrac = Math.max(0, minFrac); |
| |
| // Save values into internal state |
| // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE |
| rReqPos = -minFrac; |
| } |
| |
| @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 |
| public void roundToIncrement(BigDecimal roundingInterval, MathContext mathContext) { |
| BigDecimal d = |
| (primary == -1) ? fallback : new BigDecimal(primary).scaleByPowerOfTen(primaryScale); |
| if (isNegative()) d = d.negate(); |
| d = d.divide(roundingInterval, 0, mathContext.getRoundingMode()).multiply(roundingInterval); |
| if (isNegative()) d = d.negate(); |
| fallback = d; |
| primary = -1; |
| } |
| |
| @Override |
| public void roundToNickel(int roundingMagnitude, MathContext mathContext) { |
| BigDecimal nickel = BigDecimal.valueOf(5).scaleByPowerOfTen(roundingMagnitude); |
| roundToIncrement(nickel, mathContext); |
| } |
| |
| @Override |
| public void roundToMagnitude(int roundingMagnitude, MathContext mathContext) { |
| if (roundingMagnitude < -1000) { |
| roundToInfinity(); |
| return; |
| } |
| if (primary == -1) { |
| if (isNegative()) fallback = fallback.negate(); |
| fallback = fallback.setScale(-roundingMagnitude, mathContext.getRoundingMode()); |
| if (isNegative()) fallback = fallback.negate(); |
| // Enforce the math context. |
| fallback = fallback.round(mathContext); |
| } else { |
| int relativeScale = primaryScale - roundingMagnitude; |
| if (relativeScale < -18) { |
| // No digits will remain after rounding the number. |
| primary = 0L; |
| primaryScale = roundingMagnitude; |
| primaryPrecision = 0; |
| } else if (relativeScale < 0) { |
| // This is the harder case, when we need to perform the rounding logic. |
| // First check if the rightmost digits are already zero, where we can skip rounding. |
| if ((primary % POWERS_OF_TEN[0 - relativeScale]) == 0) { |
| // No rounding is necessary. |
| } else { |
| // TODO: Make this more efficient. Temporarily, convert to a BigDecimal and back again. |
| BigDecimal temp = new BigDecimal(primary).scaleByPowerOfTen(primaryScale); |
| if (isNegative()) temp = temp.negate(); |
| temp = temp.setScale(-roundingMagnitude, mathContext.getRoundingMode()); |
| if (isNegative()) temp = temp.negate(); |
| temp = temp.scaleByPowerOfTen(-roundingMagnitude); |
| primary = temp.longValueExact(); // should never throw |
| primaryScale = roundingMagnitude; |
| primaryPrecision = computePrecision(primary); |
| } |
| } else { |
| // No rounding is necessary. All digits are to the left of the rounding magnitude. |
| } |
| // Enforce the math context. |
| primary = new BigDecimal(primary).round(mathContext).longValueExact(); |
| primaryPrecision = computePrecision(primary); |
| } |
| } |
| |
| @Override |
| public void roundToInfinity() { |
| // noop |
| } |
| |
| /** |
| * Multiply the internal number by the specified multiplicand. This method forces the internal |
| * representation into a BigDecimal. If you are multiplying by a power of 10, use {@link |
| * #adjustMagnitude} instead. |
| * |
| * @param multiplicand The number to be passed to {@link BigDecimal#multiply}. |
| */ |
| @Override |
| public void multiplyBy(BigDecimal multiplicand) { |
| convertToBigDecimal(); |
| fallback = fallback.multiply(multiplicand); |
| if (fallback.compareTo(BigDecimal.ZERO) < 0) { |
| setNegative(!isNegative()); |
| fallback = fallback.negate(); |
| } |
| } |
| |
| @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 |
| * #adjustMagnitude} instead. |
| * |
| * @param divisor The number to be passed to {@link BigDecimal#divide}. |
| * @param scale The scale of the final rounded number. More negative means more decimal places. |
| * @param mathContext The math context to use if rounding is necessary. |
| */ |
| @SuppressWarnings("unused") |
| private void divideBy(BigDecimal divisor, int scale, MathContext mathContext) { |
| convertToBigDecimal(); |
| // Negate the scale because BigDecimal's scale is defined as the inverse of our scale |
| fallback = fallback.divide(divisor, -scale, mathContext.getRoundingMode()); |
| if (fallback.compareTo(BigDecimal.ZERO) < 0) { |
| setNegative(!isNegative()); |
| fallback = fallback.negate(); |
| } |
| } |
| |
| @Override |
| public boolean isZeroish() { |
| if (primary == -1) { |
| return fallback.compareTo(BigDecimal.ZERO) == 0; |
| } else { |
| return primary == 0; |
| } |
| } |
| |
| /** @return The power of ten of the highest digit represented by this DecimalQuantity */ |
| @Override |
| public int getMagnitude() throws ArithmeticException { |
| int scale = (primary == -1) ? scaleBigDecimal(fallback) : primaryScale; |
| int precision = (primary == -1) ? precisionBigDecimal(fallback) : primaryPrecision; |
| if (precision == 0) { |
| throw new ArithmeticException("Magnitude is not well-defined for zero"); |
| } else { |
| return scale + precision - 1; |
| } |
| } |
| |
| /** |
| * Changes the magnitude of this DecimalQuantity. If the indices of the represented digits had been |
| * previously specified, those indices are moved relative to the DecimalQuantity. |
| * |
| * <p>This method does NOT perform rounding. |
| * |
| * @param delta The number of powers of ten to shift (positive shifts to the left). |
| */ |
| @Override |
| public void adjustMagnitude(int delta) { |
| if (primary == -1) { |
| fallback = fallback.scaleByPowerOfTen(delta); |
| } else { |
| primaryScale = addOrMaxValue(primaryScale, delta); |
| } |
| } |
| |
| private static int addOrMaxValue(int a, int b) { |
| // Check for overflow, and return min/max value if overflow occurs. |
| if (b < 0 && a + b > a) { |
| return Integer.MIN_VALUE; |
| } else if (b > 0 && a + b < a) { |
| return Integer.MAX_VALUE; |
| } |
| return a + b; |
| } |
| |
| /** @return If the number represented by this DecimalQuantity is less than zero */ |
| @Override |
| public boolean isNegative() { |
| return (flags & NEGATIVE_FLAG) != 0; |
| } |
| |
| @Override |
| public Signum signum() { |
| boolean isZero = (isZeroish() && !isInfinite()); |
| boolean isNeg = isNegative(); |
| if (isZero && isNeg) { |
| return Signum.NEG_ZERO; |
| } else if (isZero) { |
| return Signum.POS_ZERO; |
| } else if (isNeg) { |
| return Signum.NEG; |
| } else { |
| return Signum.POS; |
| } |
| } |
| |
| private void setNegative(boolean isNegative) { |
| flags = (flags & (~NEGATIVE_FLAG)) | (isNegative ? NEGATIVE_FLAG : 0); |
| } |
| |
| @Override |
| public boolean isInfinite() { |
| return (flags & INFINITY_FLAG) != 0; |
| } |
| |
| private void setInfinity(boolean isInfinity) { |
| flags = (flags & (~INFINITY_FLAG)) | (isInfinity ? INFINITY_FLAG : 0); |
| } |
| |
| @Override |
| public boolean isNaN() { |
| return (flags & NAN_FLAG) != 0; |
| } |
| |
| private void setNaN(boolean isNaN) { |
| flags = (flags & (~NAN_FLAG)) | (isNaN ? NAN_FLAG : 0); |
| } |
| |
| /** |
| * Returns a representation of this DecimalQuantity as a double, with possible loss of information. |
| */ |
| @Override |
| public double toDouble() { |
| double result; |
| if (primary == -1) { |
| result = fallback.doubleValue(); |
| } else { |
| // TODO: Make this more efficient |
| result = primary; |
| for (int i = 0; i < primaryScale; i++) { |
| result *= 10.; |
| } |
| for (int i = 0; i > primaryScale; i--) { |
| result /= 10.; |
| } |
| } |
| return isNegative() ? -result : result; |
| } |
| |
| @Override |
| public BigDecimal toBigDecimal() { |
| BigDecimal result; |
| if (primary != -1) { |
| result = new BigDecimal(primary).scaleByPowerOfTen(primaryScale); |
| } else { |
| result = fallback; |
| } |
| return isNegative() ? result.negate() : result; |
| } |
| |
| @Override |
| public StandardPlural getStandardPlural(PluralRules rules) { |
| if (rules == null) { |
| // Fail gracefully if the user didn't provide a PluralRules |
| return StandardPlural.OTHER; |
| } else { |
| // TODO: Avoid converting to a double for the sake of PluralRules |
| String ruleString = rules.select(toDouble()); |
| return StandardPlural.orOtherFromString(ruleString); |
| } |
| } |
| |
| @Override |
| public double getPluralOperand(Operand operand) { |
| // TODO: This is a temporary hack. |
| return new PluralRules.FixedDecimal(toDouble()).getPluralOperand(operand); |
| } |
| |
| public boolean hasNextFraction() { |
| if (rReqPos < 0) { |
| // We are in the required zone. |
| return true; |
| } else if (rOptPos >= 0) { |
| // We are in the forbidden zone. |
| return false; |
| } else { |
| // We are in the optional zone. |
| if (primary == -1) { |
| return fallback.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) > 0; |
| } else { |
| if (primaryScale <= -19) { |
| // The number is a fraction so small that it consists of only fraction digits. |
| return primary > 0; |
| } else if (primaryScale < 0) { |
| // Check if we have a fraction part. |
| long factor = POWERS_OF_TEN[0 - primaryScale]; |
| return ((primary % factor) != 0); |
| } else { |
| // The lowest digit in the long has magnitude greater than -1. |
| return false; |
| } |
| } |
| } |
| } |
| |
| public byte nextFraction() { |
| byte returnValue; |
| if (primary == -1) { |
| BigDecimal temp = fallback.multiply(BigDecimal.TEN); |
| returnValue = temp.setScale(0, RoundingMode.FLOOR).remainder(BigDecimal.TEN).byteValue(); |
| fallback = fallback.setScale(0, RoundingMode.FLOOR).add(temp.remainder(BigDecimal.ONE)); |
| } else { |
| if (primaryScale <= -20) { |
| // The number is a fraction so small that it has no first fraction digit. |
| primaryScale += 1; |
| returnValue = 0; |
| } else if (primaryScale < 0) { |
| // Extract the fraction digit out of the middle of the long. |
| long factor = POWERS_OF_TEN[0 - primaryScale - 1]; |
| long temp1 = primary / factor; |
| long temp2 = primary % factor; |
| returnValue = (byte) (temp1 % 10); // not necessarily nonzero |
| primary = ((temp1 / 10) * factor) + temp2; |
| primaryScale += 1; |
| if (temp1 != 0) { |
| primaryPrecision -= 1; |
| } |
| } else { |
| // The lowest digit in the long has magnitude greater than -1. |
| returnValue = 0; |
| } |
| } |
| |
| // Update digit brackets |
| if (lOptPos < 0) { |
| lOptPos += 1; |
| } |
| if (lReqPos < 0) { |
| lReqPos += 1; |
| } |
| if (rReqPos < 0) { |
| rReqPos += 1; |
| } |
| if (rOptPos < 0) { |
| rOptPos += 1; |
| } |
| |
| assert returnValue >= 0; |
| return returnValue; |
| } |
| |
| public boolean hasNextInteger() { |
| if (lReqPos > 0) { |
| // We are in the required zone. |
| return true; |
| } else if (lOptPos <= 0) { |
| // We are in the forbidden zone. |
| return false; |
| } else { |
| // We are in the optional zone. |
| if (primary == -1) { |
| return fallback.setScale(0, RoundingMode.FLOOR).compareTo(BigDecimal.ZERO) > 0; |
| } else { |
| if (primaryScale < -18) { |
| // The number is a fraction so small that it has no integer part. |
| return false; |
| } else if (primaryScale < 0) { |
| // Check if we have an integer part. |
| long factor = POWERS_OF_TEN[0 - primaryScale]; |
| return ((primary % factor) != primary); // equivalent: ((primary / 10) != 0) |
| } else { |
| // The lowest digit in the long has magnitude of at least 0. |
| return primary != 0; |
| } |
| } |
| } |
| } |
| |
| private int integerCount() { |
| int digitsRemaining; |
| if (primary == -1) { |
| digitsRemaining = precisionBigDecimal(fallback) + scaleBigDecimal(fallback); |
| } else { |
| digitsRemaining = primaryPrecision + primaryScale; |
| } |
| return Math.min(Math.max(digitsRemaining, lReqPos), lOptPos); |
| } |
| |
| private int fractionCount() { |
| // TODO: This is temporary. |
| DecimalQuantity_SimpleStorage copy = new DecimalQuantity_SimpleStorage(this); |
| int fractionCount = 0; |
| while (copy.hasNextFraction()) { |
| copy.nextFraction(); |
| fractionCount++; |
| } |
| return fractionCount; |
| } |
| |
| @Override |
| public int getUpperDisplayMagnitude() { |
| return integerCount() - 1; |
| } |
| |
| @Override |
| public int getLowerDisplayMagnitude() { |
| return -fractionCount(); |
| } |
| |
| // @Override |
| // public byte getIntegerDigit(int index) { |
| // return getDigitPos(index); |
| // } |
| // |
| // @Override |
| // public byte getFractionDigit(int index) { |
| // return getDigitPos(-index - 1); |
| // } |
| |
| @Override |
| public byte getDigit(int magnitude) { |
| // TODO: This is temporary. |
| DecimalQuantity_SimpleStorage copy = new DecimalQuantity_SimpleStorage(this); |
| if (magnitude < 0) { |
| for (int p = -1; p > magnitude; p--) { |
| copy.nextFraction(); |
| } |
| return copy.nextFraction(); |
| } else { |
| for (int p = 0; p < magnitude; p++) { |
| copy.nextInteger(); |
| } |
| return copy.nextInteger(); |
| } |
| } |
| |
| public byte nextInteger() { |
| byte returnValue; |
| if (primary == -1) { |
| returnValue = fallback.setScale(0, RoundingMode.FLOOR).remainder(BigDecimal.TEN).byteValue(); |
| BigDecimal temp = fallback.divide(BigDecimal.TEN).setScale(0, RoundingMode.FLOOR); |
| fallback = fallback.remainder(BigDecimal.ONE).add(temp); |
| } else { |
| if (primaryScale < -18) { |
| // The number is a fraction so small that it has no integer part. |
| returnValue = 0; |
| } else if (primaryScale < 0) { |
| // Extract the integer digit out of the middle of the long. In many ways, this is the heart |
| // of the digit iterator algorithm. |
| long factor = POWERS_OF_TEN[0 - primaryScale]; |
| if ((primary % factor) != primary) { // equivalent: ((primary / 10) != 0) |
| returnValue = (byte) ((primary / factor) % 10); |
| long temp = (primary / 10); |
| primary = temp - (temp % factor) + (primary % factor); |
| primaryPrecision -= 1; |
| } else { |
| returnValue = 0; |
| } |
| } else if (primaryScale == 0) { |
| // Fast-path for primaryScale == 0 (otherwise equivalent to previous step). |
| if (primary != 0) { |
| returnValue = (byte) (primary % 10); |
| primary /= 10; |
| primaryPrecision -= 1; |
| } else { |
| returnValue = 0; |
| } |
| } else { |
| // The lowest digit in the long has magnitude greater than 0. |
| primaryScale -= 1; |
| returnValue = 0; |
| } |
| } |
| |
| // Update digit brackets |
| if (lOptPos > 0) { |
| lOptPos -= 1; |
| } |
| if (lReqPos > 0) { |
| lReqPos -= 1; |
| } |
| if (rReqPos > 0) { |
| rReqPos -= 1; |
| } |
| if (rOptPos > 0) { |
| rOptPos -= 1; |
| } |
| |
| assert returnValue >= 0; |
| return returnValue; |
| } |
| |
| /** |
| * Helper method to compute the precision of a BigDecimal by our definition of precision, which is |
| * that the number zero gets precision zero. |
| * |
| * @param decimal The BigDecimal whose precision to compute. |
| * @return The precision by our definition. |
| */ |
| private static int precisionBigDecimal(BigDecimal decimal) { |
| if (decimal.compareTo(BigDecimal.ZERO) == 0) { |
| return 0; |
| } else { |
| return decimal.precision(); |
| } |
| } |
| |
| /** |
| * Helper method to compute the scale of a BigDecimal by our definition of scale, which is that |
| * deeper fractions result in negative scales as opposed to positive scales. |
| * |
| * @param decimal The BigDecimal whose scale to compute. |
| * @return The scale by our definition. |
| */ |
| private static int scaleBigDecimal(BigDecimal decimal) { |
| return -decimal.scale(); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("<DecimalQuantity1 "); |
| if (primary == -1) { |
| sb.append(lOptPos > 1000 ? "max" : lOptPos); |
| sb.append(":"); |
| sb.append(lReqPos); |
| sb.append(":"); |
| sb.append(rReqPos); |
| sb.append(":"); |
| sb.append(rOptPos < -1000 ? "min" : rOptPos); |
| sb.append(" "); |
| sb.append(fallback.toString()); |
| } else { |
| String digits = Long.toString(primary); |
| int iDec = digits.length() + primaryScale; |
| int iLP = iDec - toRange(lOptPos, -1000, 1000); |
| int iLB = iDec - toRange(lReqPos, -1000, 1000); |
| int iRB = iDec - toRange(rReqPos, -1000, 1000); |
| int iRP = iDec - toRange(rOptPos, -1000, 1000); |
| iDec = Math.max(Math.min(iDec, digits.length() + 1), -1); |
| iLP = Math.max(Math.min(iLP, digits.length() + 1), -1); |
| iLB = Math.max(Math.min(iLB, digits.length() + 1), -1); |
| iRB = Math.max(Math.min(iRB, digits.length() + 1), -1); |
| iRP = Math.max(Math.min(iRP, digits.length() + 1), -1); |
| |
| for (int i = -1; i <= digits.length() + 1; i++) { |
| if (i == iLP) sb.append('('); |
| if (i == iLB) sb.append('['); |
| if (i == iDec) sb.append('.'); |
| if (i == iRB) sb.append(']'); |
| if (i == iRP) sb.append(')'); |
| if (i >= 0 && i < digits.length()) sb.append(digits.charAt(i)); |
| else sb.append('\u00A0'); |
| } |
| } |
| sb.append(">"); |
| return sb.toString(); |
| } |
| |
| @Override |
| public String toPlainString() { |
| // NOTE: This logic is duplicated between here and DecimalQuantity_AbstractBCD. |
| StringBuilder sb = new StringBuilder(); |
| if (isNegative()) { |
| sb.append('-'); |
| } |
| int upper = getUpperDisplayMagnitude(); |
| int lower = getLowerDisplayMagnitude(); |
| int p = upper; |
| for (; p >= 0; p--) { |
| sb.append((char) ('0' + getDigit(p))); |
| } |
| if (lower < 0) { |
| sb.append('.'); |
| } |
| for(; p >= lower; p--) { |
| sb.append((char) ('0' + getDigit(p))); |
| } |
| return sb.toString(); |
| } |
| |
| private static int toRange(int i, int lo, int hi) { |
| if (i < lo) { |
| return lo; |
| } else if (i > hi) { |
| return hi; |
| } else { |
| return i; |
| } |
| } |
| |
| @Override |
| public void populateUFieldPosition(FieldPosition fp) { |
| if (fp instanceof UFieldPosition) { |
| ((UFieldPosition) fp) |
| .setFractionDigits((int) getPluralOperand(Operand.v), (long) getPluralOperand(Operand.f)); |
| } |
| } |
| |
| @Override |
| public int getExponent() { |
| return origPrimaryScale; |
| } |
| |
| @Override |
| public void adjustExponent(int delta) { |
| origPrimaryScale = origPrimaryScale + delta; |
| } |
| } |