DecimalQuantity_SimpleStorage.java revision fe77e7203e518f62b5bd8e8c603bca361e9cf47b
1// © 2017 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3package com.ibm.icu.impl.number;
4
5import java.math.BigDecimal;
6import java.math.MathContext;
7import java.math.RoundingMode;
8import java.text.FieldPosition;
9
10import com.ibm.icu.impl.StandardPlural;
11import com.ibm.icu.text.PluralRules;
12import com.ibm.icu.text.PluralRules.Operand;
13import com.ibm.icu.text.UFieldPosition;
14
15/**
16 * This is an older implementation of DecimalQuantity. A newer, faster implementation is
17 * DecimalQuantity2. I kept this implementation around because it was useful for testing purposes
18 * (being able to compare the output of one implementation with the other).
19 *
20 * <p>This class is NOT IMMUTABLE and NOT THREAD SAFE and is intended to be used by a single thread
21 * to format a number through a formatter, which is thread-safe.
22 */
23public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
24  // Four positions: left optional '(', left required '[', right required ']', right optional ')'.
25  // These four positions determine which digits are displayed in the output string.  They do NOT
26  // affect rounding.  These positions are internal-only and can be specified only by the public
27  // endpoints like setFractionLength, setIntegerLength, and setSignificantDigits, among others.
28  //
29  //   * Digits between lReqPos and rReqPos are in the "required zone" and are always displayed.
30  //   * Digits between lOptPos and rOptPos but outside the required zone are in the "optional zone"
31  //     and are displayed unless they are trailing off the left or right edge of the number and
32  //     have a numerical value of zero.  In order to be "trailing", the digits need to be beyond
33  //     the decimal point in their respective directions.
34  //   * Digits outside of the "optional zone" are never displayed.
35  //
36  // See the table below for illustrative examples.
37  //
38  // +---------+---------+---------+---------+------------+------------------------+--------------+
39  // | lOptPos | lReqPos | rReqPos | rOptPos |   number   |        positions       | en-US string |
40  // +---------+---------+---------+---------+------------+------------------------+--------------+
41  // |    5    |    2    |   -1    |   -5    |   1234.567 |     ( 12[34.5]67  )    |   1,234.567  |
42  // |    3    |    2    |   -1    |   -5    |   1234.567 |      1(2[34.5]67  )    |     234.567  |
43  // |    3    |    2    |   -1    |   -2    |   1234.567 |      1(2[34.5]6)7      |     234.56   |
44  // |    6    |    4    |    2    |   -5    | 123456789. |  123(45[67]89.     )   | 456,789.     |
45  // |    6    |    4    |    2    |    1    | 123456789. |     123(45[67]8)9.     | 456,780.     |
46  // |   -1    |   -1    |   -3    |   -4    | 0.123456   |     0.1([23]4)56       |        .0234 |
47  // |    6    |    4    |   -2    |   -2    |     12.3   |     (  [  12.3 ])      |    0012.30   |
48  // +---------+---------+---------+---------+------------+------------------------+--------------+
49  //
50  private int lOptPos = Integer.MAX_VALUE;
51  private int lReqPos = 0;
52  private int rReqPos = 0;
53  private int rOptPos = Integer.MIN_VALUE;
54
55  // Internally, attempt to use a long to store the number. A long can hold numbers between 18 and
56  // 19 digits, covering the vast majority of use cases. We store three values: the long itself,
57  // the "scale" of the long (the power of 10 represented by the rightmost digit in the long), and
58  // the "precision" (the number of digits in the long). "primary" and "primaryScale" are the only
59  // two variables that are required for representing the number in memory. "primaryPrecision" is
60  // saved only for the sake of performance enhancements when performing certain operations. It can
61  // always be re-computed from "primary" and "primaryScale".
62  private long primary;
63  private int primaryScale;
64  private int primaryPrecision;
65
66  // If the decimal can't fit into the long, fall back to a BigDecimal.
67  private BigDecimal fallback;
68
69  // Other properties
70  private int flags;
71  private static final int NEGATIVE_FLAG = 1;
72  private static final int INFINITY_FLAG = 2;
73  private static final int NAN_FLAG = 4;
74  private static final long[] POWERS_OF_TEN = {
75    1L,
76    10L,
77    100L,
78    1000L,
79    10000L,
80    100000L,
81    1000000L,
82    10000000L,
83    100000000L,
84    1000000000L,
85    10000000000L,
86    100000000000L,
87    1000000000000L,
88    10000000000000L,
89    100000000000000L,
90    1000000000000000L,
91    10000000000000000L,
92    100000000000000000L,
93    1000000000000000000L
94  };
95
96  @Override
97  public int maxRepresentableDigits() {
98    return Integer.MAX_VALUE;
99  }
100
101  public DecimalQuantity_SimpleStorage(long input) {
102    if (input < 0) {
103      setNegative(true);
104      input *= -1;
105    }
106
107    primary = input;
108    primaryScale = 0;
109    primaryPrecision = computePrecision(primary);
110    fallback = null;
111  }
112
113  /**
114   * Creates a DecimalQuantity from the given double value. Internally attempts several strategies
115   * for converting the double to an exact representation, falling back on a BigDecimal if it fails
116   * to do so.
117   *
118   * @param input The double to represent by this DecimalQuantity.
119   */
120  public DecimalQuantity_SimpleStorage(double input) {
121    if (input < 0) {
122      setNegative(true);
123      input *= -1;
124    }
125
126    // First try reading from IEEE bits. This is trivial only for doubles in [2^52, 2^64). If it
127    // fails, we wasted only a few CPU cycles.
128    long ieeeBits = Double.doubleToLongBits(input);
129    int exponent = (int) ((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff;
130    if (exponent >= 52 && exponent <= 63) {
131      // We can convert this double directly to a long.
132      long mantissa = (ieeeBits & 0x000fffffffffffffL) + 0x0010000000000000L;
133      primary = (mantissa << (exponent - 52));
134      primaryScale = 0;
135      primaryPrecision = computePrecision(primary);
136      return;
137    }
138
139    // Now try parsing the string produced by Double.toString().
140    String temp = Double.toString(input);
141    try {
142      if (temp.length() == 3 && temp.equals("0.0")) {
143        // Case 1: Zero.
144        primary = 0L;
145        primaryScale = 0;
146        primaryPrecision = 0;
147      } else if (temp.indexOf('E') != -1) {
148        // Case 2: Exponential notation.
149        assert temp.indexOf('.') == 1;
150        int expPos = temp.indexOf('E');
151        primary = Long.parseLong(temp.charAt(0) + temp.substring(2, expPos));
152        primaryScale = Integer.parseInt(temp.substring(expPos + 1)) - (expPos - 1) + 1;
153        primaryPrecision = expPos - 1;
154      } else if (temp.charAt(0) == '0') {
155        // Case 3: Fraction-only number.
156        assert temp.indexOf('.') == 1;
157        primary = Long.parseLong(temp.substring(2)); // ignores leading zeros
158        primaryScale = 2 - temp.length();
159        primaryPrecision = computePrecision(primary);
160      } else if (temp.charAt(temp.length() - 1) == '0') {
161        // Case 4: Integer-only number.
162        assert temp.indexOf('.') == temp.length() - 2;
163        int rightmostNonzeroDigitIndex = temp.length() - 3;
164        while (temp.charAt(rightmostNonzeroDigitIndex) == '0') {
165          rightmostNonzeroDigitIndex -= 1;
166        }
167        primary = Long.parseLong(temp.substring(0, rightmostNonzeroDigitIndex + 1));
168        primaryScale = temp.length() - rightmostNonzeroDigitIndex - 3;
169        primaryPrecision = rightmostNonzeroDigitIndex + 1;
170      } else if (temp.equals("Infinity")) {
171        // Case 5: Infinity.
172        primary = 0;
173        setInfinity(true);
174      } else if (temp.equals("NaN")) {
175        // Case 6: NaN.
176        primary = 0;
177        setNaN(true);
178      } else {
179        // Case 7: Number with both a fraction and an integer.
180        int decimalPos = temp.indexOf('.');
181        primary = Long.parseLong(temp.substring(0, decimalPos) + temp.substring(decimalPos + 1));
182        primaryScale = decimalPos - temp.length() + 1;
183        primaryPrecision = temp.length() - 1;
184      }
185    } catch (NumberFormatException e) {
186      // The digits of the double can't fit into the long.
187      primary = -1;
188      fallback = new BigDecimal(temp);
189    }
190  }
191
192  static final double LOG_2_OF_TEN = 3.32192809489;
193
194  public DecimalQuantity_SimpleStorage(double input, boolean fast) {
195    if (input < 0) {
196      setNegative(true);
197      input *= -1;
198    }
199
200    // Our strategy is to read all digits that are *guaranteed* to be valid without delving into
201    // the IEEE rounding rules.  This strategy might not end up with a perfect representation of
202    // the fractional part of the double.
203    long ieeeBits = Double.doubleToLongBits(input);
204    int exponent = (int) ((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff;
205    long mantissa = (ieeeBits & 0x000fffffffffffffL) + 0x0010000000000000L;
206    if (exponent > 63) {
207      throw new IllegalArgumentException(); // FIXME
208    } else if (exponent >= 52) {
209      primary = (mantissa << (exponent - 52));
210      primaryScale = 0;
211      primaryPrecision = computePrecision(primary);
212      return;
213    } else if (exponent >= 0) {
214      int shift = 52 - exponent;
215      primary = (mantissa >> shift); // integer part
216      int fractionCount = (int) (shift / LOG_2_OF_TEN);
217      long fraction = (mantissa - (primary << shift)) + 1L; // TODO: Explain the +1L
218      primary *= POWERS_OF_TEN[fractionCount];
219      for (int i = 0; i < fractionCount; i++) {
220        long times10 = (fraction * 10L);
221        long digit = times10 >> shift;
222        assert digit >= 0 && digit < 10;
223        primary += digit * POWERS_OF_TEN[fractionCount - i - 1];
224        fraction = times10 & ((1L << shift) - 1);
225      }
226      primaryScale = -fractionCount;
227      primaryPrecision = computePrecision(primary);
228    } else {
229      throw new IllegalArgumentException(); // FIXME
230    }
231  }
232
233  public DecimalQuantity_SimpleStorage(BigDecimal decimal) {
234    setToBigDecimal(decimal);
235  }
236
237  public DecimalQuantity_SimpleStorage(DecimalQuantity_SimpleStorage other) {
238    copyFrom(other);
239  }
240
241  @Override
242  public void setToBigDecimal(BigDecimal decimal) {
243    if (decimal.compareTo(BigDecimal.ZERO) < 0) {
244      setNegative(true);
245      decimal = decimal.negate();
246    }
247
248    primary = -1;
249    if (decimal.compareTo(BigDecimal.ZERO) == 0) {
250      fallback = BigDecimal.ZERO;
251    } else {
252      fallback = decimal;
253    }
254  }
255
256  @Override
257  public DecimalQuantity_SimpleStorage createCopy() {
258    return new DecimalQuantity_SimpleStorage(this);
259  }
260
261  /**
262   * Make the internal state of this DecimalQuantity equal to another DecimalQuantity.
263   *
264   * @param other The template DecimalQuantity. All properties from this DecimalQuantity will be
265   *     copied into this DecimalQuantity.
266   */
267  @Override
268  public void copyFrom(DecimalQuantity other) {
269    // TODO: Check before casting
270    DecimalQuantity_SimpleStorage _other = (DecimalQuantity_SimpleStorage) other;
271    lOptPos = _other.lOptPos;
272    lReqPos = _other.lReqPos;
273    rReqPos = _other.rReqPos;
274    rOptPos = _other.rOptPos;
275    primary = _other.primary;
276    primaryScale = _other.primaryScale;
277    primaryPrecision = _other.primaryPrecision;
278    fallback = _other.fallback;
279    flags = _other.flags;
280  }
281
282  @Override
283  public long getPositionFingerprint() {
284    long fingerprint = 0;
285    fingerprint ^= lOptPos;
286    fingerprint ^= (lReqPos << 16);
287    fingerprint ^= ((long) rReqPos << 32);
288    fingerprint ^= ((long) rOptPos << 48);
289    return fingerprint;
290  }
291
292  /**
293   * Utility method to compute the number of digits ("precision") in a long.
294   *
295   * @param input The long (which can't contain more than 19 digits).
296   * @return The precision of the long.
297   */
298  private static int computePrecision(long input) {
299    int precision = 0;
300    while (input > 0) {
301      input /= 10;
302      precision++;
303    }
304    return precision;
305  }
306
307  /**
308   * Changes the internal representation from a long to a BigDecimal. Used only for operations that
309   * don't support longs.
310   */
311  private void convertToBigDecimal() {
312    if (primary == -1) {
313      return;
314    }
315
316    fallback = new BigDecimal(primary).scaleByPowerOfTen(primaryScale);
317    primary = -1;
318  }
319
320  @Override
321  public void setIntegerLength(int minInt, int maxInt) {
322    // Graceful failures for bogus input
323    minInt = Math.max(0, minInt);
324    maxInt = Math.max(0, maxInt);
325
326    // The minima must be less than or equal to the maxima
327    if (maxInt < minInt) {
328      minInt = maxInt;
329    }
330
331    // Save values into internal state
332    // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
333    lOptPos = maxInt;
334    lReqPos = minInt;
335  }
336
337  @Override
338  public void setFractionLength(int minFrac, int maxFrac) {
339    // Graceful failures for bogus input
340    minFrac = Math.max(0, minFrac);
341    maxFrac = Math.max(0, maxFrac);
342
343    // The minima must be less than or equal to the maxima
344    if (maxFrac < minFrac) {
345      minFrac = maxFrac;
346    }
347
348    // Save values into internal state
349    // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
350    rReqPos = -minFrac;
351    rOptPos = -maxFrac;
352  }
353
354  @Override
355  public void roundToIncrement(BigDecimal roundingInterval, MathContext mathContext) {
356    BigDecimal d =
357        (primary == -1) ? fallback : new BigDecimal(primary).scaleByPowerOfTen(primaryScale);
358    if (isNegative()) d = d.negate();
359    d = d.divide(roundingInterval, 0, mathContext.getRoundingMode()).multiply(roundingInterval);
360    if (isNegative()) d = d.negate();
361    fallback = d;
362    primary = -1;
363  }
364
365  @Override
366  public void roundToMagnitude(int roundingMagnitude, MathContext mathContext) {
367    if (roundingMagnitude < -1000) {
368      roundToInfinity();
369      return;
370    }
371    if (primary == -1) {
372      if (isNegative()) fallback = fallback.negate();
373      fallback = fallback.setScale(-roundingMagnitude, mathContext.getRoundingMode());
374      if (isNegative()) fallback = fallback.negate();
375      // Enforce the math context.
376      fallback = fallback.round(mathContext);
377    } else {
378      int relativeScale = primaryScale - roundingMagnitude;
379      if (relativeScale < -18) {
380        // No digits will remain after rounding the number.
381        primary = 0L;
382        primaryScale = roundingMagnitude;
383        primaryPrecision = 0;
384      } else if (relativeScale < 0) {
385        // This is the harder case, when we need to perform the rounding logic.
386        // First check if the rightmost digits are already zero, where we can skip rounding.
387        if ((primary % POWERS_OF_TEN[0 - relativeScale]) == 0) {
388          // No rounding is necessary.
389        } else {
390          // TODO: Make this more efficient. Temporarily, convert to a BigDecimal and back again.
391          BigDecimal temp = new BigDecimal(primary).scaleByPowerOfTen(primaryScale);
392          if (isNegative()) temp = temp.negate();
393          temp = temp.setScale(-roundingMagnitude, mathContext.getRoundingMode());
394          if (isNegative()) temp = temp.negate();
395          temp = temp.scaleByPowerOfTen(-roundingMagnitude);
396          primary = temp.longValueExact(); // should never throw
397          primaryScale = roundingMagnitude;
398          primaryPrecision = computePrecision(primary);
399        }
400      } else {
401        // No rounding is necessary. All digits are to the left of the rounding magnitude.
402      }
403      // Enforce the math context.
404      primary = new BigDecimal(primary).round(mathContext).longValueExact();
405      primaryPrecision = computePrecision(primary);
406    }
407  }
408
409  @Override
410  public void roundToInfinity() {
411    // noop
412  }
413
414  /**
415   * Multiply the internal number by the specified multiplicand. This method forces the internal
416   * representation into a BigDecimal. If you are multiplying by a power of 10, use {@link
417   * #adjustMagnitude} instead.
418   *
419   * @param multiplicand The number to be passed to {@link BigDecimal#multiply}.
420   */
421  @Override
422  public void multiplyBy(BigDecimal multiplicand) {
423    convertToBigDecimal();
424    fallback = fallback.multiply(multiplicand);
425    if (fallback.compareTo(BigDecimal.ZERO) < 0) {
426      setNegative(!isNegative());
427      fallback = fallback.negate();
428    }
429  }
430
431  /**
432   * Divide the internal number by the specified quotient. This method forces the internal
433   * representation into a BigDecimal. If you are dividing by a power of 10, use {@link
434   * #adjustMagnitude} instead.
435   *
436   * @param divisor The number to be passed to {@link BigDecimal#divide}.
437   * @param scale The scale of the final rounded number. More negative means more decimal places.
438   * @param mathContext The math context to use if rounding is necessary.
439   */
440  @SuppressWarnings("unused")
441  private void divideBy(BigDecimal divisor, int scale, MathContext mathContext) {
442    convertToBigDecimal();
443    // Negate the scale because BigDecimal's scale is defined as the inverse of our scale
444    fallback = fallback.divide(divisor, -scale, mathContext.getRoundingMode());
445    if (fallback.compareTo(BigDecimal.ZERO) < 0) {
446      setNegative(!isNegative());
447      fallback = fallback.negate();
448    }
449  }
450
451  @Override
452  public boolean isZero() {
453    if (primary == -1) {
454      return fallback.compareTo(BigDecimal.ZERO) == 0;
455    } else {
456      return primary == 0;
457    }
458  }
459
460  /** @return The power of ten of the highest digit represented by this DecimalQuantity */
461  @Override
462  public int getMagnitude() throws ArithmeticException {
463    int scale = (primary == -1) ? scaleBigDecimal(fallback) : primaryScale;
464    int precision = (primary == -1) ? precisionBigDecimal(fallback) : primaryPrecision;
465    if (precision == 0) {
466      throw new ArithmeticException("Magnitude is not well-defined for zero");
467    } else {
468      return scale + precision - 1;
469    }
470  }
471
472  /**
473   * Changes the magnitude of this DecimalQuantity. If the indices of the represented digits had been
474   * previously specified, those indices are moved relative to the DecimalQuantity.
475   *
476   * <p>This method does NOT perform rounding.
477   *
478   * @param delta The number of powers of ten to shift (positive shifts to the left).
479   */
480  @Override
481  public void adjustMagnitude(int delta) {
482    if (primary == -1) {
483      fallback = fallback.scaleByPowerOfTen(delta);
484    } else {
485      primaryScale = addOrMaxValue(primaryScale, delta);
486    }
487  }
488
489  private static int addOrMaxValue(int a, int b) {
490    // Check for overflow, and return min/max value if overflow occurs.
491    if (b < 0 && a + b > a) {
492      return Integer.MIN_VALUE;
493    } else if (b > 0 && a + b < a) {
494      return Integer.MAX_VALUE;
495    }
496    return a + b;
497  }
498
499  /** @return If the number represented by this DecimalQuantity is less than zero */
500  @Override
501  public boolean isNegative() {
502    return (flags & NEGATIVE_FLAG) != 0;
503  }
504
505  private void setNegative(boolean isNegative) {
506    flags = (flags & (~NEGATIVE_FLAG)) | (isNegative ? NEGATIVE_FLAG : 0);
507  }
508
509  @Override
510  public boolean isInfinite() {
511    return (flags & INFINITY_FLAG) != 0;
512  }
513
514  private void setInfinity(boolean isInfinity) {
515    flags = (flags & (~INFINITY_FLAG)) | (isInfinity ? INFINITY_FLAG : 0);
516  }
517
518  @Override
519  public boolean isNaN() {
520    return (flags & NAN_FLAG) != 0;
521  }
522
523  private void setNaN(boolean isNaN) {
524    flags = (flags & (~NAN_FLAG)) | (isNaN ? NAN_FLAG : 0);
525  }
526
527  /**
528   * Returns a representation of this DecimalQuantity as a double, with possible loss of information.
529   */
530  @Override
531  public double toDouble() {
532    double result;
533    if (primary == -1) {
534      result = fallback.doubleValue();
535    } else {
536      // TODO: Make this more efficient
537      result = primary;
538      for (int i = 0; i < primaryScale; i++) {
539        result *= 10.;
540      }
541      for (int i = 0; i > primaryScale; i--) {
542        result /= 10.;
543      }
544    }
545    return isNegative() ? -result : result;
546  }
547
548  @Override
549  public BigDecimal toBigDecimal() {
550    BigDecimal result;
551    if (primary != -1) {
552      result = new BigDecimal(primary).scaleByPowerOfTen(primaryScale);
553    } else {
554      result = fallback;
555    }
556    return isNegative() ? result.negate() : result;
557  }
558
559  @Override
560  public StandardPlural getStandardPlural(PluralRules rules) {
561    if (rules == null) {
562      // Fail gracefully if the user didn't provide a PluralRules
563      return StandardPlural.OTHER;
564    } else {
565      // TODO: Avoid converting to a double for the sake of PluralRules
566      String ruleString = rules.select(toDouble());
567      return StandardPlural.orOtherFromString(ruleString);
568    }
569  }
570
571  @Override
572  public double getPluralOperand(Operand operand) {
573    // TODO: This is a temporary hack.
574    return new PluralRules.FixedDecimal(toDouble()).getPluralOperand(operand);
575  }
576
577  public boolean hasNextFraction() {
578    if (rReqPos < 0) {
579      // We are in the required zone.
580      return true;
581    } else if (rOptPos >= 0) {
582      // We are in the forbidden zone.
583      return false;
584    } else {
585      // We are in the optional zone.
586      if (primary == -1) {
587        return fallback.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) > 0;
588      } else {
589        if (primaryScale <= -19) {
590          // The number is a fraction so small that it consists of only fraction digits.
591          return primary > 0;
592        } else if (primaryScale < 0) {
593          // Check if we have a fraction part.
594          long factor = POWERS_OF_TEN[0 - primaryScale];
595          return ((primary % factor) != 0);
596        } else {
597          // The lowest digit in the long has magnitude greater than -1.
598          return false;
599        }
600      }
601    }
602  }
603
604  public byte nextFraction() {
605    byte returnValue;
606    if (primary == -1) {
607      BigDecimal temp = fallback.multiply(BigDecimal.TEN);
608      returnValue = temp.setScale(0, RoundingMode.FLOOR).remainder(BigDecimal.TEN).byteValue();
609      fallback = fallback.setScale(0, RoundingMode.FLOOR).add(temp.remainder(BigDecimal.ONE));
610    } else {
611      if (primaryScale <= -20) {
612        // The number is a fraction so small that it has no first fraction digit.
613        primaryScale += 1;
614        returnValue = 0;
615      } else if (primaryScale < 0) {
616        // Extract the fraction digit out of the middle of the long.
617        long factor = POWERS_OF_TEN[0 - primaryScale - 1];
618        long temp1 = primary / factor;
619        long temp2 = primary % factor;
620        returnValue = (byte) (temp1 % 10); // not necessarily nonzero
621        primary = ((temp1 / 10) * factor) + temp2;
622        primaryScale += 1;
623        if (temp1 != 0) {
624          primaryPrecision -= 1;
625        }
626      } else {
627        // The lowest digit in the long has magnitude greater than -1.
628        returnValue = 0;
629      }
630    }
631
632    // Update digit brackets
633    if (lOptPos < 0) {
634      lOptPos += 1;
635    }
636    if (lReqPos < 0) {
637      lReqPos += 1;
638    }
639    if (rReqPos < 0) {
640      rReqPos += 1;
641    }
642    if (rOptPos < 0) {
643      rOptPos += 1;
644    }
645
646    assert returnValue >= 0;
647    return returnValue;
648  }
649
650  public boolean hasNextInteger() {
651    if (lReqPos > 0) {
652      // We are in the required zone.
653      return true;
654    } else if (lOptPos <= 0) {
655      // We are in the forbidden zone.
656      return false;
657    } else {
658      // We are in the optional zone.
659      if (primary == -1) {
660        return fallback.setScale(0, RoundingMode.FLOOR).compareTo(BigDecimal.ZERO) > 0;
661      } else {
662        if (primaryScale < -18) {
663          // The number is a fraction so small that it has no integer part.
664          return false;
665        } else if (primaryScale < 0) {
666          // Check if we have an integer part.
667          long factor = POWERS_OF_TEN[0 - primaryScale];
668          return ((primary % factor) != primary); // equivalent: ((primary / 10) != 0)
669        } else {
670          // The lowest digit in the long has magnitude of at least 0.
671          return primary != 0;
672        }
673      }
674    }
675  }
676
677  private int integerCount() {
678    int digitsRemaining;
679    if (primary == -1) {
680      digitsRemaining = precisionBigDecimal(fallback) + scaleBigDecimal(fallback);
681    } else {
682      digitsRemaining = primaryPrecision + primaryScale;
683    }
684    return Math.min(Math.max(digitsRemaining, lReqPos), lOptPos);
685  }
686
687  private int fractionCount() {
688    // TODO: This is temporary.
689    DecimalQuantity_SimpleStorage copy = new DecimalQuantity_SimpleStorage(this);
690    int fractionCount = 0;
691    while (copy.hasNextFraction()) {
692      copy.nextFraction();
693      fractionCount++;
694    }
695    return fractionCount;
696  }
697
698  @Override
699  public int getUpperDisplayMagnitude() {
700    return integerCount() - 1;
701  }
702
703  @Override
704  public int getLowerDisplayMagnitude() {
705    return -fractionCount();
706  }
707
708  //  @Override
709  //  public byte getIntegerDigit(int index) {
710  //    return getDigitPos(index);
711  //  }
712  //
713  //  @Override
714  //  public byte getFractionDigit(int index) {
715  //    return getDigitPos(-index - 1);
716  //  }
717
718  @Override
719  public byte getDigit(int magnitude) {
720    // TODO: This is temporary.
721    DecimalQuantity_SimpleStorage copy = new DecimalQuantity_SimpleStorage(this);
722    if (magnitude < 0) {
723      for (int p = -1; p > magnitude; p--) {
724        copy.nextFraction();
725      }
726      return copy.nextFraction();
727    } else {
728      for (int p = 0; p < magnitude; p++) {
729        copy.nextInteger();
730      }
731      return copy.nextInteger();
732    }
733  }
734
735  public byte nextInteger() {
736    byte returnValue;
737    if (primary == -1) {
738      returnValue = fallback.setScale(0, RoundingMode.FLOOR).remainder(BigDecimal.TEN).byteValue();
739      BigDecimal temp = fallback.divide(BigDecimal.TEN).setScale(0, RoundingMode.FLOOR);
740      fallback = fallback.remainder(BigDecimal.ONE).add(temp);
741    } else {
742      if (primaryScale < -18) {
743        // The number is a fraction so small that it has no integer part.
744        returnValue = 0;
745      } else if (primaryScale < 0) {
746        // Extract the integer digit out of the middle of the long. In many ways, this is the heart
747        // of the digit iterator algorithm.
748        long factor = POWERS_OF_TEN[0 - primaryScale];
749        if ((primary % factor) != primary) { // equivalent: ((primary / 10) != 0)
750          returnValue = (byte) ((primary / factor) % 10);
751          long temp = (primary / 10);
752          primary = temp - (temp % factor) + (primary % factor);
753          primaryPrecision -= 1;
754        } else {
755          returnValue = 0;
756        }
757      } else if (primaryScale == 0) {
758        // Fast-path for primaryScale == 0 (otherwise equivalent to previous step).
759        if (primary != 0) {
760          returnValue = (byte) (primary % 10);
761          primary /= 10;
762          primaryPrecision -= 1;
763        } else {
764          returnValue = 0;
765        }
766      } else {
767        // The lowest digit in the long has magnitude greater than 0.
768        primaryScale -= 1;
769        returnValue = 0;
770      }
771    }
772
773    // Update digit brackets
774    if (lOptPos > 0) {
775      lOptPos -= 1;
776    }
777    if (lReqPos > 0) {
778      lReqPos -= 1;
779    }
780    if (rReqPos > 0) {
781      rReqPos -= 1;
782    }
783    if (rOptPos > 0) {
784      rOptPos -= 1;
785    }
786
787    assert returnValue >= 0;
788    return returnValue;
789  }
790
791  /**
792   * Helper method to compute the precision of a BigDecimal by our definition of precision, which is
793   * that the number zero gets precision zero.
794   *
795   * @param decimal The BigDecimal whose precision to compute.
796   * @return The precision by our definition.
797   */
798  private static int precisionBigDecimal(BigDecimal decimal) {
799    if (decimal.compareTo(BigDecimal.ZERO) == 0) {
800      return 0;
801    } else {
802      return decimal.precision();
803    }
804  }
805
806  /**
807   * Helper method to compute the scale of a BigDecimal by our definition of scale, which is that
808   * deeper fractions result in negative scales as opposed to positive scales.
809   *
810   * @param decimal The BigDecimal whose scale to compute.
811   * @return The scale by our definition.
812   */
813  private static int scaleBigDecimal(BigDecimal decimal) {
814    return -decimal.scale();
815  }
816
817  @Override
818  public String toString() {
819    StringBuilder sb = new StringBuilder();
820    sb.append("<DecimalQuantity1 ");
821    if (primary == -1) {
822      sb.append(lOptPos > 1000 ? "max" : lOptPos);
823      sb.append(":");
824      sb.append(lReqPos);
825      sb.append(":");
826      sb.append(rReqPos);
827      sb.append(":");
828      sb.append(rOptPos < -1000 ? "min" : rOptPos);
829      sb.append(" ");
830      sb.append(fallback.toString());
831    } else {
832      String digits = Long.toString(primary);
833      int iDec = digits.length() + primaryScale;
834      int iLP = iDec - toRange(lOptPos, -1000, 1000);
835      int iLB = iDec - toRange(lReqPos, -1000, 1000);
836      int iRB = iDec - toRange(rReqPos, -1000, 1000);
837      int iRP = iDec - toRange(rOptPos, -1000, 1000);
838      iDec = Math.max(Math.min(iDec, digits.length() + 1), -1);
839      iLP = Math.max(Math.min(iLP, digits.length() + 1), -1);
840      iLB = Math.max(Math.min(iLB, digits.length() + 1), -1);
841      iRB = Math.max(Math.min(iRB, digits.length() + 1), -1);
842      iRP = Math.max(Math.min(iRP, digits.length() + 1), -1);
843
844      for (int i = -1; i <= digits.length() + 1; i++) {
845        if (i == iLP) sb.append('(');
846        if (i == iLB) sb.append('[');
847        if (i == iDec) sb.append('.');
848        if (i == iRB) sb.append(']');
849        if (i == iRP) sb.append(')');
850        if (i >= 0 && i < digits.length()) sb.append(digits.charAt(i));
851        else sb.append('\u00A0');
852      }
853    }
854    sb.append(">");
855    return sb.toString();
856  }
857
858  @Override
859  public String toPlainString() {
860      // NOTE: This logic is duplicated between here and DecimalQuantity_AbstractBCD.
861      StringBuilder sb = new StringBuilder();
862      if (isNegative()) {
863          sb.append('-');
864      }
865      for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) {
866        sb.append(getDigit(m));
867        if (m == 0) sb.append('.');
868      }
869      return sb.toString();
870  }
871
872  private static int toRange(int i, int lo, int hi) {
873    if (i < lo) {
874      return lo;
875    } else if (i > hi) {
876      return hi;
877    } else {
878      return i;
879    }
880  }
881
882  @Override
883  public void populateUFieldPosition(FieldPosition fp) {
884    if (fp instanceof UFieldPosition) {
885      ((UFieldPosition) fp)
886          .setFractionDigits((int) getPluralOperand(Operand.v), (long) getPluralOperand(Operand.f));
887    }
888  }
889}
890