| // © 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.math.BigInteger; |
| import java.math.MathContext; |
| import java.text.FieldPosition; |
| |
| import com.ibm.icu.impl.StandardPlural; |
| import com.ibm.icu.impl.Utility; |
| import com.ibm.icu.text.PluralRules; |
| import com.ibm.icu.text.PluralRules.Operand; |
| import com.ibm.icu.text.UFieldPosition; |
| |
| /** |
| * Represents numbers and digit display properties using Binary Coded Decimal (BCD). |
| * |
| * @implements {@link DecimalQuantity} |
| */ |
| public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity { |
| |
| /** |
| * The power of ten corresponding to the least significant digit in the BCD. For example, if this |
| * object represents the number "3.14", the BCD will be "0x314" and the scale will be -2. |
| * |
| * <p> |
| * Note that in {@link java.math.BigDecimal}, the scale is defined differently: the number of digits |
| * after the decimal place, which is the negative of our definition of scale. |
| */ |
| protected int scale; |
| |
| /** |
| * The number of digits in the BCD. For example, "1007" has BCD "0x1007" and precision 4. A long |
| * cannot represent precisions greater than 16. |
| * |
| * <p> |
| * This value must be re-calculated whenever the value in bcd changes by using |
| * {@link #computePrecisionAndCompact()}. |
| */ |
| protected int precision; |
| |
| /** |
| * A bitmask of properties relating to the number represented by this object. |
| * |
| * @see #NEGATIVE_FLAG |
| * @see #INFINITY_FLAG |
| * @see #NAN_FLAG |
| */ |
| protected byte flags; |
| |
| protected static final int NEGATIVE_FLAG = 1; |
| 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 DecimalQuantityBCD, 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 DecimalQuantity 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; |
| |
| // Positions to keep track of leading and trailing zeros. |
| // lReqPos is the magnitude of the first required leading zero. |
| // rReqPos is the magnitude of the last required trailing zero. |
| protected int lReqPos = 0; |
| protected int rReqPos = 0; |
| |
| @Override |
| public void copyFrom(DecimalQuantity _other) { |
| copyBcdFrom(_other); |
| DecimalQuantity_AbstractBCD other = (DecimalQuantity_AbstractBCD) _other; |
| lReqPos = other.lReqPos; |
| rReqPos = other.rReqPos; |
| scale = other.scale; |
| precision = other.precision; |
| flags = other.flags; |
| origDouble = other.origDouble; |
| origDelta = other.origDelta; |
| isApproximate = other.isApproximate; |
| } |
| |
| public DecimalQuantity_AbstractBCD clear() { |
| lReqPos = 0; |
| rReqPos = 0; |
| flags = 0; |
| setBcdToZero(); // sets scale, precision, hasDouble, origDouble, origDelta, and BCD data |
| return this; |
| } |
| |
| @Override |
| public void setMinInteger(int minInt) { |
| // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class. |
| assert minInt >= 0; |
| |
| // Special behavior: do not set minInt to be less than what is already set. |
| // This is so significant digits rounding can set the integer length. |
| if (minInt < lReqPos) { |
| minInt = lReqPos; |
| } |
| |
| // Save values into internal state |
| lReqPos = minInt; |
| } |
| |
| @Override |
| public void setMinFraction(int minFrac) { |
| // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class. |
| assert minFrac >= 0; |
| |
| // 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) { |
| // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. |
| assert maxInt >= 0; |
| |
| if (precision == 0) { |
| return; |
| } |
| |
| int magnitude = getMagnitude(); |
| if (maxInt <= magnitude) { |
| popFromLeft(magnitude - maxInt + 1); |
| compact(); |
| } |
| } |
| |
| @Override |
| public long getPositionFingerprint() { |
| long fingerprint = 0; |
| fingerprint ^= (lReqPos << 16); |
| fingerprint ^= ((long) rReqPos << 32); |
| return fingerprint; |
| } |
| |
| @Override |
| public void roundToIncrement(BigDecimal roundingIncrement, MathContext mathContext) { |
| // Do not call this method with an increment having only a 1 or a 5 digit! |
| // Use a more efficient call to either roundToMagnitude() or roundToNickel(). |
| // Note: The check, which is somewhat expensive, is performed in an assertion |
| // to disable it in production. |
| assert roundingIncrement.stripTrailingZeros().precision() != 1 |
| || roundingIncrement.stripTrailingZeros().unscaledValue().intValue() != 5 |
| || roundingIncrement.stripTrailingZeros().unscaledValue().intValue() != 1; |
| BigDecimal temp = toBigDecimal(); |
| temp = temp.divide(roundingIncrement, 0, mathContext.getRoundingMode()) |
| .multiply(roundingIncrement) |
| .round(mathContext); |
| if (temp.signum() == 0) { |
| setBcdToZero(); // keeps negative flag for -0.0 |
| } else { |
| setToBigDecimal(temp); |
| } |
| } |
| |
| @Override |
| public void multiplyBy(BigDecimal multiplicand) { |
| if (isInfinite() || isZero() || isNaN()) { |
| return; |
| } |
| BigDecimal temp = toBigDecimal(); |
| temp = temp.multiply(multiplicand); |
| setToBigDecimal(temp); |
| } |
| |
| @Override |
| public void negate() { |
| flags ^= NEGATIVE_FLAG; |
| } |
| |
| @Override |
| public int getMagnitude() throws ArithmeticException { |
| if (precision == 0) { |
| throw new ArithmeticException("Magnitude is not well-defined for zero"); |
| } else { |
| return scale + precision - 1; |
| } |
| } |
| |
| @Override |
| public void adjustMagnitude(int delta) { |
| if (precision != 0) { |
| scale = Utility.addExact(scale, delta); |
| origDelta = Utility.addExact(origDelta, delta); |
| } |
| } |
| |
| @Override |
| public StandardPlural getStandardPlural(PluralRules rules) { |
| if (rules == null) { |
| // Fail gracefully if the user didn't provide a PluralRules |
| return StandardPlural.OTHER; |
| } else { |
| @SuppressWarnings("deprecation") |
| String ruleString = rules.select(this); |
| return StandardPlural.orOtherFromString(ruleString); |
| } |
| } |
| |
| @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: |
| // Invert the negative sign if necessary |
| return isNegative() ? -toLong(true) : toLong(true); |
| case f: |
| return toFractionLong(true); |
| case t: |
| return toFractionLong(false); |
| case v: |
| return fractionCount(); |
| case w: |
| return fractionCountWithoutTrailingZeros(); |
| default: |
| return Math.abs(toDouble()); |
| } |
| } |
| |
| @Override |
| public void populateUFieldPosition(FieldPosition fp) { |
| if (fp instanceof UFieldPosition) { |
| ((UFieldPosition) fp).setFractionDigits((int) getPluralOperand(Operand.v), |
| (long) getPluralOperand(Operand.f)); |
| } |
| } |
| |
| @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 : 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 : 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); |
| } |
| |
| private int fractionCount() { |
| return -getLowerDisplayMagnitude(); |
| } |
| |
| private int fractionCountWithoutTrailingZeros() { |
| return Math.max(-scale, 0); |
| } |
| |
| @Override |
| public boolean isNegative() { |
| return (flags & NEGATIVE_FLAG) != 0; |
| } |
| |
| @Override |
| public int signum() { |
| return isNegative() ? -1 : isZero() ? 0 : 1; |
| } |
| |
| @Override |
| public boolean isInfinite() { |
| return (flags & INFINITY_FLAG) != 0; |
| } |
| |
| @Override |
| public boolean isNaN() { |
| return (flags & NAN_FLAG) != 0; |
| } |
| |
| @Override |
| public boolean isZero() { |
| return precision == 0; |
| } |
| |
| public void setToInt(int n) { |
| setBcdToZero(); |
| flags = 0; |
| if (n < 0) { |
| flags |= NEGATIVE_FLAG; |
| n = -n; |
| } |
| if (n != 0) { |
| _setToInt(n); |
| compact(); |
| } |
| } |
| |
| private void _setToInt(int n) { |
| if (n == Integer.MIN_VALUE) { |
| readLongToBcd(-(long) n); |
| } else { |
| readIntToBcd(n); |
| } |
| } |
| |
| public void setToLong(long n) { |
| setBcdToZero(); |
| flags = 0; |
| if (n < 0) { |
| flags |= NEGATIVE_FLAG; |
| n = -n; |
| } |
| if (n != 0) { |
| _setToLong(n); |
| compact(); |
| } |
| } |
| |
| private void _setToLong(long n) { |
| if (n == Long.MIN_VALUE) { |
| readBigIntegerToBcd(BigInteger.valueOf(n).negate()); |
| } else if (n <= Integer.MAX_VALUE) { |
| readIntToBcd((int) n); |
| } else { |
| readLongToBcd(n); |
| } |
| } |
| |
| public void setToBigInteger(BigInteger n) { |
| setBcdToZero(); |
| flags = 0; |
| if (n.signum() == -1) { |
| flags |= NEGATIVE_FLAG; |
| n = n.negate(); |
| } |
| if (n.signum() != 0) { |
| _setToBigInteger(n); |
| compact(); |
| } |
| } |
| |
| private void _setToBigInteger(BigInteger n) { |
| if (n.bitLength() < 32) { |
| readIntToBcd(n.intValue()); |
| } else if (n.bitLength() < 64) { |
| readLongToBcd(n.longValue()); |
| } else { |
| readBigIntegerToBcd(n); |
| } |
| } |
| |
| /** |
| * Sets the internal BCD state to represent the value in the given double. |
| * |
| * @param n |
| * The value to consume. |
| */ |
| public void setToDouble(double n) { |
| setBcdToZero(); |
| flags = 0; |
| // Double.compare() handles +0.0 vs -0.0 |
| if (Double.compare(n, 0.0) < 0) { |
| flags |= NEGATIVE_FLAG; |
| n = -n; |
| } |
| if (Double.isNaN(n)) { |
| flags |= NAN_FLAG; |
| } else if (Double.isInfinite(n)) { |
| flags |= INFINITY_FLAG; |
| } else if (n != 0) { |
| _setToDoubleFast(n); |
| compact(); |
| } |
| } |
| |
| private static final double[] DOUBLE_MULTIPLIERS = { |
| 1e0, |
| 1e1, |
| 1e2, |
| 1e3, |
| 1e4, |
| 1e5, |
| 1e6, |
| 1e7, |
| 1e8, |
| 1e9, |
| 1e10, |
| 1e11, |
| 1e12, |
| 1e13, |
| 1e14, |
| 1e15, |
| 1e16, |
| 1e17, |
| 1e18, |
| 1e19, |
| 1e20, |
| 1e21 }; |
| |
| /** |
| * Uses double multiplication and division to get the number into integer space before converting to |
| * digits. Since double arithmetic is inexact, the resulting digits may not be accurate. |
| */ |
| private void _setToDoubleFast(double n) { |
| isApproximate = true; |
| origDouble = n; |
| origDelta = 0; |
| |
| // NOTE: Unlike ICU4C, doubles are always IEEE 754 doubles. |
| long ieeeBits = Double.doubleToLongBits(n); |
| int exponent = (int) ((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff; |
| |
| // Not all integers can be represented exactly for exponent > 52 |
| if (exponent <= 52 && (long) n == n) { |
| _setToLong((long) n); |
| return; |
| } |
| |
| // 3.3219... is log2(10) |
| int fracLength = (int) ((52 - exponent) / 3.32192809489); |
| if (fracLength >= 0) { |
| int i = fracLength; |
| // 1e22 is the largest exact double. |
| for (; i >= 22; i -= 22) |
| n *= 1e22; |
| n *= DOUBLE_MULTIPLIERS[i]; |
| } else { |
| int i = fracLength; |
| // 1e22 is the largest exact double. |
| for (; i <= -22; i += 22) |
| n /= 1e22; |
| n /= DOUBLE_MULTIPLIERS[-i]; |
| } |
| long result = Math.round(n); |
| if (result != 0) { |
| _setToLong(result); |
| scale -= fracLength; |
| } |
| } |
| |
| /** |
| * Uses Double.toString() to obtain an exact accurate representation of the double, overwriting it |
| * into the BCD. This method can be called at any point after {@link #_setToDoubleFast} while |
| * {@link #isApproximate} is still true. |
| */ |
| private void convertToAccurateDouble() { |
| double n = origDouble; |
| assert n != 0; |
| int delta = origDelta; |
| setBcdToZero(); |
| |
| // Call the slow oracle function (Double.toString in Java, sprintf in C++). |
| String dstr = Double.toString(n); |
| |
| if (dstr.indexOf('E') != -1) { |
| // Case 1: Exponential notation. |
| assert dstr.indexOf('.') == 1; |
| int expPos = dstr.indexOf('E'); |
| _setToLong(Long.parseLong(dstr.charAt(0) + dstr.substring(2, expPos))); |
| scale += Integer.parseInt(dstr.substring(expPos + 1)) - (expPos - 1) + 1; |
| } else if (dstr.charAt(0) == '0') { |
| // Case 2: Fraction-only number. |
| assert dstr.indexOf('.') == 1; |
| _setToLong(Long.parseLong(dstr.substring(2))); |
| scale += 2 - dstr.length(); |
| } else if (dstr.charAt(dstr.length() - 1) == '0') { |
| // Case 3: Integer-only number. |
| // Note: this path should not normally happen, because integer-only numbers are captured |
| // before the approximate double logic is performed. |
| assert dstr.indexOf('.') == dstr.length() - 2; |
| assert dstr.length() - 2 <= 18; |
| _setToLong(Long.parseLong(dstr.substring(0, dstr.length() - 2))); |
| // no need to adjust scale |
| } else { |
| // Case 4: Number with both a fraction and an integer. |
| int decimalPos = dstr.indexOf('.'); |
| _setToLong(Long.parseLong(dstr.substring(0, decimalPos) + dstr.substring(decimalPos + 1))); |
| scale += decimalPos - dstr.length() + 1; |
| } |
| |
| scale += delta; |
| compact(); |
| explicitExactDouble = true; |
| } |
| |
| /** |
| * Whether this {@link DecimalQuantity_DualStorageBCD} has been explicitly converted to an exact |
| * double. true if backed by a double that was explicitly converted via convertToAccurateDouble; |
| * false otherwise. Used for testing. |
| * |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Deprecated |
| public boolean explicitExactDouble = false; |
| |
| /** |
| * Sets the internal BCD state to represent the value in the given BigDecimal. |
| * |
| * @param n |
| * The value to consume. |
| */ |
| @Override |
| public void setToBigDecimal(BigDecimal n) { |
| setBcdToZero(); |
| flags = 0; |
| if (n.signum() == -1) { |
| flags |= NEGATIVE_FLAG; |
| n = n.negate(); |
| } |
| if (n.signum() != 0) { |
| _setToBigDecimal(n); |
| compact(); |
| } |
| } |
| |
| private void _setToBigDecimal(BigDecimal n) { |
| int fracLength = n.scale(); |
| n = n.scaleByPowerOfTen(fracLength); |
| BigInteger bi = n.toBigInteger(); |
| _setToBigInteger(bi); |
| scale -= fracLength; |
| } |
| |
| /** |
| * Returns a long approximating the internal BCD. A long can only represent the integral part of the |
| * number. |
| * |
| * @param truncateIfOverflow if false and the number does NOT fit, fails with an assertion error. |
| * @return A 64-bit integer representation of the internal BCD. |
| */ |
| public long toLong(boolean truncateIfOverflow) { |
| // NOTE: Call sites should be guarded by fitsInLong(), like this: |
| // if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ } |
| // Fallback behavior upon truncateIfOverflow is to truncate at 17 digits. |
| assert(truncateIfOverflow || fitsInLong()); |
| long result = 0L; |
| int upperMagnitude = scale + precision - 1; |
| if (truncateIfOverflow) { |
| upperMagnitude = Math.min(upperMagnitude, 17); |
| } |
| for (int magnitude = upperMagnitude; magnitude >= 0; magnitude--) { |
| result = result * 10 + getDigitPos(magnitude - scale); |
| } |
| if (isNegative()) { |
| result = -result; |
| } |
| return result; |
| } |
| |
| /** |
| * This returns a long representing the fraction digits of the number, as required by PluralRules. |
| * For example, if we represent the number "1.20" (including optional and required digits), then this |
| * function returns "20" if includeTrailingZeros is true or "2" if false. |
| */ |
| public long toFractionLong(boolean includeTrailingZeros) { |
| long result = 0L; |
| int magnitude = -1; |
| int lowerMagnitude = scale; |
| if (includeTrailingZeros) { |
| lowerMagnitude = Math.min(lowerMagnitude, rReqPos); |
| } |
| // NOTE: Java has only signed longs, so we check result <= 1e17 instead of 1e18 |
| for (; magnitude >= lowerMagnitude && result <= 1e17; magnitude--) { |
| result = result * 10 + getDigitPos(magnitude - scale); |
| } |
| // Remove trailing zeros; this can happen during integer overflow cases. |
| if (!includeTrailingZeros) { |
| while (result > 0 && (result % 10) == 0) { |
| result /= 10; |
| } |
| } |
| return result; |
| } |
| |
| static final byte[] INT64_BCD = { 9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8 }; |
| |
| /** |
| * Returns whether or not a Long can fully represent the value stored in this DecimalQuantity. |
| */ |
| public boolean fitsInLong() { |
| if (isZero()) { |
| return true; |
| } |
| if (scale < 0) { |
| return false; |
| } |
| int magnitude = getMagnitude(); |
| if (magnitude < 18) { |
| return true; |
| } |
| if (magnitude > 18) { |
| return false; |
| } |
| // Hard case: the magnitude is 10^18. |
| // The largest int64 is: 9,223,372,036,854,775,807 |
| for (int p = 0; p < precision; p++) { |
| byte digit = getDigit(18 - p); |
| if (digit < INT64_BCD[p]) { |
| return true; |
| } else if (digit > INT64_BCD[p]) { |
| return false; |
| } |
| } |
| // Exactly equal to max long plus one. |
| return isNegative(); |
| } |
| |
| /** |
| * Returns a double approximating the internal BCD. The double may not retain all of the information |
| * encoded in the BCD if the BCD represents a number out of range of a double. |
| * |
| * @return A double representation of the internal BCD. |
| */ |
| @Override |
| public double toDouble() { |
| // 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; |
| |
| if (isNaN()) { |
| return Double.NaN; |
| } else if (isInfinite()) { |
| return isNegative() ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| toScientificString(sb); |
| return Double.valueOf(sb.toString()); |
| } |
| |
| @Override |
| public BigDecimal toBigDecimal() { |
| if (isApproximate) { |
| // Converting to a BigDecimal requires Double.toString(). |
| convertToAccurateDouble(); |
| } |
| return bcdToBigDecimal(); |
| } |
| |
| private static int safeSubtract(int a, int b) { |
| int diff = a - b; |
| if (b < 0 && diff < a) |
| return Integer.MAX_VALUE; |
| if (b > 0 && diff > a) |
| return Integer.MIN_VALUE; |
| return diff; |
| } |
| |
| private static final int SECTION_LOWER_EDGE = -1; |
| private static final int SECTION_UPPER_EDGE = -2; |
| |
| /** Removes all fraction digits. */ |
| public void truncate() { |
| if (scale < 0) { |
| shiftRight(-scale); |
| scale = 0; |
| compact(); |
| } |
| } |
| |
| @Override |
| public void roundToNickel(int magnitude, MathContext mathContext) { |
| roundToMagnitude(magnitude, mathContext, true); |
| } |
| |
| @Override |
| public void roundToMagnitude(int magnitude, MathContext mathContext) { |
| roundToMagnitude(magnitude, mathContext, false); |
| } |
| |
| private void roundToMagnitude(int magnitude, MathContext mathContext, boolean nickel) { |
| // The position in the BCD at which rounding will be performed; digits to the right of position |
| // will be rounded away. |
| int position = safeSubtract(magnitude, scale); |
| |
| // Enforce the number of digits required by the MathContext. |
| int _mcPrecision = mathContext.getPrecision(); |
| if (_mcPrecision > 0 && precision - _mcPrecision > position) { |
| position = precision - _mcPrecision; |
| } |
| |
| // "trailing" = least significant digit to the left of rounding |
| byte trailingDigit = getDigitPos(position); |
| |
| if (position <= 0 && !isApproximate && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { |
| // All digits are to the left of the rounding magnitude. |
| } else if (precision == 0) { |
| // No rounding for zero. |
| } else { |
| // Perform rounding logic. |
| // "leading" = most significant digit to the right of rounding |
| byte leadingDigit = getDigitPos(safeSubtract(position, 1)); |
| |
| // Compute which section of the number we are in. |
| // EDGE means we are at the bottom or top edge, like 1.000 or 1.999 (used by doubles) |
| // LOWER means we are between the bottom edge and the midpoint, like 1.391 |
| // MIDPOINT means we are exactly in the middle, like 1.500 |
| // UPPER means we are between the midpoint and the top edge, like 1.916 |
| int section; |
| if (!isApproximate) { |
| if (nickel && trailingDigit != 2 && trailingDigit != 7) { |
| // Nickel rounding, and not at .02x or .07x |
| if (trailingDigit < 2) { |
| // .00, .01 => down to .00 |
| section = RoundingUtils.SECTION_LOWER; |
| } else if (trailingDigit < 5) { |
| // .03, .04 => up to .05 |
| section = RoundingUtils.SECTION_UPPER; |
| } else if (trailingDigit < 7) { |
| // .05, .06 => down to .05 |
| section = RoundingUtils.SECTION_LOWER; |
| } else { |
| // .08, .09 => up to .10 |
| section = RoundingUtils.SECTION_UPPER; |
| } |
| } else if (leadingDigit < 5) { |
| // Includes nickel rounding .020-.024 and .070-.074 |
| section = RoundingUtils.SECTION_LOWER; |
| } else if (leadingDigit > 5) { |
| // Includes nickel rounding .026-.029 and .076-.079 |
| section = RoundingUtils.SECTION_UPPER; |
| } else { |
| // Includes nickel rounding .025 and .075 |
| section = RoundingUtils.SECTION_MIDPOINT; |
| for (int p = safeSubtract(position, 2); p >= 0; p--) { |
| if (getDigitPos(p) != 0) { |
| section = RoundingUtils.SECTION_UPPER; |
| break; |
| } |
| } |
| } |
| } else { |
| int p = safeSubtract(position, 2); |
| int minP = Math.max(0, precision - 14); |
| if (leadingDigit == 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { |
| section = SECTION_LOWER_EDGE; |
| for (; p >= minP; p--) { |
| if (getDigitPos(p) != 0) { |
| section = RoundingUtils.SECTION_LOWER; |
| break; |
| } |
| } |
| } else if (leadingDigit == 4 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { |
| section = RoundingUtils.SECTION_MIDPOINT; |
| for (; p >= minP; p--) { |
| if (getDigitPos(p) != 9) { |
| section = RoundingUtils.SECTION_LOWER; |
| break; |
| } |
| } |
| } else if (leadingDigit == 5 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { |
| section = RoundingUtils.SECTION_MIDPOINT; |
| for (; p >= minP; p--) { |
| if (getDigitPos(p) != 0) { |
| section = RoundingUtils.SECTION_UPPER; |
| break; |
| } |
| } |
| } else if (leadingDigit == 9 && (!nickel || trailingDigit == 4 || trailingDigit == 9)) { |
| section = SECTION_UPPER_EDGE; |
| for (; p >= minP; p--) { |
| if (getDigitPos(p) != 9) { |
| section = RoundingUtils.SECTION_UPPER; |
| break; |
| } |
| } |
| } else if (nickel && trailingDigit != 2 && trailingDigit != 7) { |
| // Nickel rounding, and not at .02x or .07x |
| if (trailingDigit < 2) { |
| // .00, .01 => down to .00 |
| section = RoundingUtils.SECTION_LOWER; |
| } else if (trailingDigit < 5) { |
| // .03, .04 => up to .05 |
| section = RoundingUtils.SECTION_UPPER; |
| } else if (trailingDigit < 7) { |
| // .05, .06 => down to .05 |
| section = RoundingUtils.SECTION_LOWER; |
| } else { |
| // .08, .09 => up to .10 |
| section = RoundingUtils.SECTION_UPPER; |
| } |
| } else if (leadingDigit < 5) { |
| // Includes nickel rounding .020-.024 and .070-.074 |
| section = RoundingUtils.SECTION_LOWER; |
| } else { |
| // Includes nickel rounding .026-.029 and .076-.079 |
| section = RoundingUtils.SECTION_UPPER; |
| } |
| |
| boolean roundsAtMidpoint = RoundingUtils |
| .roundsAtMidpoint(mathContext.getRoundingMode().ordinal()); |
| if (safeSubtract(position, 1) < precision - 14 |
| || (roundsAtMidpoint && section == RoundingUtils.SECTION_MIDPOINT) |
| || (!roundsAtMidpoint && section < 0 /* i.e. at upper or lower edge */)) { |
| // Oops! This means that we have to get the exact representation of the double, |
| // because the zone of uncertainty is along the rounding boundary. |
| convertToAccurateDouble(); |
| roundToMagnitude(magnitude, mathContext, nickel); // start over |
| return; |
| } |
| |
| // Turn off the approximate double flag, since the value is now confirmed to be exact. |
| isApproximate = false; |
| origDouble = 0.0; |
| origDelta = 0; |
| |
| if (position <= 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { |
| // All digits are to the left of the rounding magnitude. |
| return; |
| } |
| |
| // Good to continue rounding. |
| if (section == SECTION_LOWER_EDGE) |
| section = RoundingUtils.SECTION_LOWER; |
| if (section == SECTION_UPPER_EDGE) |
| section = RoundingUtils.SECTION_UPPER; |
| } |
| |
| // Nickel rounding "half even" goes to the nearest whole (away from the 5). |
| boolean isEven = nickel |
| ? (trailingDigit < 2 || trailingDigit > 7 |
| || (trailingDigit == 2 && section != RoundingUtils.SECTION_UPPER) |
| || (trailingDigit == 7 && section == RoundingUtils.SECTION_UPPER)) |
| : (trailingDigit % 2) == 0; |
| |
| boolean roundDown = RoundingUtils.getRoundingDirection(isEven, |
| isNegative(), |
| section, |
| mathContext.getRoundingMode().ordinal(), |
| this); |
| |
| // Perform truncation |
| if (position >= precision) { |
| setBcdToZero(); |
| scale = magnitude; |
| } else { |
| shiftRight(position); |
| } |
| |
| if (nickel) { |
| if (trailingDigit < 5 && roundDown) { |
| setDigitPos(0, (byte) 0); |
| compact(); |
| return; |
| } else if (trailingDigit >= 5 && !roundDown) { |
| setDigitPos(0, (byte) 9); |
| trailingDigit = 9; |
| // do not return: use the bubbling logic below |
| } else { |
| setDigitPos(0, (byte) 5); |
| // compact not necessary: digit at position 0 is nonzero |
| return; |
| } |
| } |
| |
| // Bubble the result to the higher digits |
| if (!roundDown) { |
| if (trailingDigit == 9) { |
| int bubblePos = 0; |
| // Note: in the long implementation, the most digits BCD can have at this point is |
| // 15, so bubblePos <= 15 and getDigitPos(bubblePos) is safe. |
| for (; getDigitPos(bubblePos) == 9; bubblePos++) { |
| } |
| shiftRight(bubblePos); // shift off the trailing 9s |
| } |
| byte digit0 = getDigitPos(0); |
| assert digit0 != 9; |
| setDigitPos(0, (byte) (digit0 + 1)); |
| precision += 1; // in case an extra digit got added |
| } |
| |
| compact(); |
| } |
| } |
| |
| @Override |
| public void roundToInfinity() { |
| if (isApproximate) { |
| convertToAccurateDouble(); |
| } |
| } |
| |
| /** |
| * Appends a digit, optionally with one or more leading zeros, to the end of the value represented by |
| * this DecimalQuantity. |
| * |
| * <p> |
| * The primary use of this method is to construct numbers during a parsing loop. It allows parsing to |
| * take advantage of the digit list infrastructure primarily designed for formatting. |
| * |
| * @param value |
| * The digit to append. |
| * @param leadingZeros |
| * The number of zeros to append before the digit. For example, if the value in this |
| * instance starts as 12.3, and you append a 4 with 1 leading zero, the value becomes |
| * 12.304. |
| * @param appendAsInteger |
| * If true, increase the magnitude of existing digits to make room for the new digit. If |
| * false, append to the end like a fraction digit. If true, there must not be any fraction |
| * digits already in the number. |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Deprecated |
| public void appendDigit(byte value, int leadingZeros, boolean appendAsInteger) { |
| assert leadingZeros >= 0; |
| |
| // Zero requires special handling to maintain the invariant that the least-significant digit |
| // in the BCD is nonzero. |
| if (value == 0) { |
| if (appendAsInteger && precision != 0) { |
| scale += leadingZeros + 1; |
| } |
| return; |
| } |
| |
| // Deal with trailing zeros |
| if (scale > 0) { |
| leadingZeros += scale; |
| if (appendAsInteger) { |
| scale = 0; |
| } |
| } |
| |
| // Append digit |
| shiftLeft(leadingZeros + 1); |
| setDigitPos(0, value); |
| |
| // Fix scale if in integer mode |
| if (appendAsInteger) { |
| scale += leadingZeros + 1; |
| } |
| } |
| |
| @Override |
| public String toPlainString() { |
| // NOTE: This logic is duplicated between here and DecimalQuantity_SimpleStorage. |
| StringBuilder sb = new StringBuilder(); |
| if (isNegative()) { |
| sb.append('-'); |
| } |
| if (precision == 0 || getMagnitude() < 0) { |
| sb.append('0'); |
| } |
| for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { |
| sb.append((char) ('0' + getDigit(m))); |
| if (m == 0) |
| sb.append('.'); |
| } |
| return sb.toString(); |
| } |
| |
| public String toScientificString() { |
| StringBuilder sb = new StringBuilder(); |
| toScientificString(sb); |
| return sb.toString(); |
| } |
| |
| public void toScientificString(StringBuilder result) { |
| assert(!isApproximate); |
| if (isNegative()) { |
| result.append('-'); |
| } |
| if (precision == 0) { |
| result.append("0E+0"); |
| return; |
| } |
| // NOTE: It is not safe to add to lOptPos (aka maxInt) or subtract from |
| // rOptPos (aka -maxFrac) due to overflow. |
| int upperPos = precision - 1; |
| int lowerPos = 0; |
| int p = upperPos; |
| result.append((char) ('0' + getDigitPos(p))); |
| if ((--p) >= lowerPos) { |
| result.append('.'); |
| for (; p >= lowerPos; p--) { |
| result.append((char) ('0' + getDigitPos(p))); |
| } |
| } |
| result.append('E'); |
| int _scale = upperPos + scale; |
| if (_scale == Integer.MIN_VALUE) { |
| result.append("-2147483648"); |
| return; |
| } else if (_scale < 0) { |
| _scale *= -1; |
| result.append('-'); |
| } else { |
| result.append('+'); |
| } |
| if (_scale == 0) { |
| result.append('0'); |
| } |
| int insertIndex = result.length(); |
| while (_scale > 0) { |
| int quot = _scale / 10; |
| int rem = _scale % 10; |
| result.insert(insertIndex, (char) ('0' + rem)); |
| _scale = quot; |
| } |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) { |
| return true; |
| } |
| if (other == null) { |
| return false; |
| } |
| if (!(other instanceof DecimalQuantity_AbstractBCD)) { |
| return false; |
| } |
| DecimalQuantity_AbstractBCD _other = (DecimalQuantity_AbstractBCD) other; |
| |
| boolean basicEquals = |
| scale == _other.scale |
| && precision == _other.precision |
| && flags == _other.flags |
| && lReqPos == _other.lReqPos |
| && rReqPos == _other.rReqPos |
| && isApproximate == _other.isApproximate; |
| if (!basicEquals) { |
| return false; |
| } |
| |
| if (precision == 0) { |
| return true; |
| } else if (isApproximate) { |
| return origDouble == _other.origDouble && origDelta == _other.origDelta; |
| } else { |
| for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { |
| if (getDigit(m) != _other.getDigit(m)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| |
| /** |
| * Returns a single digit from the BCD list. No internal state is changed by calling this method. |
| * |
| * @param position |
| * The position of the digit to pop, counted in BCD units from the least significant |
| * digit. If outside the range supported by the implementation, zero is returned. |
| * @return The digit at the specified location. |
| */ |
| protected abstract byte getDigitPos(int position); |
| |
| /** |
| * Sets the digit in the BCD list. This method only sets the digit; it is the caller's responsibility |
| * to call {@link #compact} after setting the digit. |
| * |
| * @param position |
| * The position of the digit to pop, counted in BCD units from the least significant |
| * digit. If outside the range supported by the implementation, an AssertionError is |
| * thrown. |
| * @param value |
| * The digit to set at the specified location. |
| */ |
| protected abstract void setDigitPos(int position, byte value); |
| |
| /** |
| * Adds zeros to the end of the BCD list. This will result in an invalid BCD representation; it is |
| * the caller's responsibility to do further manipulation and then call {@link #compact}. |
| * |
| * @param numDigits |
| * The number of zeros to add. |
| */ |
| protected abstract void shiftLeft(int numDigits); |
| |
| /** |
| * Removes digits from the end of the BCD list. This may result in an invalid BCD representation; it |
| * is the caller's responsibility to follow-up with a call to {@link #compact}. |
| * |
| * @param numDigits |
| * The number of digits to remove. |
| */ |
| protected abstract void shiftRight(int numDigits); |
| |
| /** |
| * Directly removes digits from the front of the BCD list. |
| * Updates precision. |
| * |
| * CAUTION: it is the caller's responsibility to call {@link #compact} after this method. |
| */ |
| protected abstract void popFromLeft(int numDigits); |
| |
| /** |
| * Sets the internal representation to zero. Clears any values stored in scale, precision, hasDouble, |
| * origDouble, origDelta, and BCD data. |
| */ |
| protected abstract void setBcdToZero(); |
| |
| /** |
| * Sets the internal BCD state to represent the value in the given int. The int is guaranteed to be |
| * either positive. The internal state is guaranteed to be empty when this method is called. |
| * |
| * @param n |
| * The value to consume. |
| */ |
| protected abstract void readIntToBcd(int input); |
| |
| /** |
| * Sets the internal BCD state to represent the value in the given long. The long is guaranteed to be |
| * either positive. The internal state is guaranteed to be empty when this method is called. |
| * |
| * @param n |
| * The value to consume. |
| */ |
| protected abstract void readLongToBcd(long input); |
| |
| /** |
| * Sets the internal BCD state to represent the value in the given BigInteger. The BigInteger is |
| * guaranteed to be positive, and it is guaranteed to be larger than Long.MAX_VALUE. The internal |
| * state is guaranteed to be empty when this method is called. |
| * |
| * @param n |
| * The value to consume. |
| */ |
| protected abstract void readBigIntegerToBcd(BigInteger input); |
| |
| /** |
| * Returns a BigDecimal encoding the internal BCD value. |
| * |
| * @return A BigDecimal representation of the internal BCD. |
| */ |
| protected abstract BigDecimal bcdToBigDecimal(); |
| |
| protected abstract void copyBcdFrom(DecimalQuantity _other); |
| |
| /** |
| * Removes trailing zeros from the BCD (adjusting the scale as required) and then computes the |
| * precision. The precision is the number of digits in the number up through the greatest nonzero |
| * digit. |
| * |
| * <p> |
| * This method must always be called when bcd changes in order for assumptions to be correct in |
| * methods like {@link #fractionCount()}. |
| */ |
| protected abstract void compact(); |
| } |