1/*
2 * Copyright (C) 2015, International Business Machines
3 * Corporation and others.  All Rights Reserved.
4 *
5 * file name: precisison.cpp
6 */
7
8#include <math.h>
9
10#include "unicode/utypes.h"
11
12#if !UCONFIG_NO_FORMATTING
13
14#include "digitlst.h"
15#include "fmtableimp.h"
16#include "precision.h"
17#include "putilimp.h"
18#include "visibledigits.h"
19
20U_NAMESPACE_BEGIN
21
22static const int32_t gPower10[] = {1, 10, 100, 1000};
23
24FixedPrecision::FixedPrecision()
25        : fExactOnly(FALSE), fFailIfOverMax(FALSE), fRoundingMode(DecimalFormat::kRoundHalfEven) {
26    fMin.setIntDigitCount(1);
27    fMin.setFracDigitCount(0);
28}
29
30UBool
31FixedPrecision::isRoundingRequired(
32        int32_t upperExponent, int32_t lowerExponent) const {
33    int32_t leastSigAllowed = fMax.getLeastSignificantInclusive();
34    int32_t maxSignificantDigits = fSignificant.getMax();
35    int32_t roundDigit;
36    if (maxSignificantDigits == INT32_MAX) {
37        roundDigit = leastSigAllowed;
38    } else {
39        int32_t limitDigit = upperExponent - maxSignificantDigits;
40        roundDigit =
41                limitDigit > leastSigAllowed ? limitDigit : leastSigAllowed;
42    }
43    return (roundDigit > lowerExponent);
44}
45
46DigitList &
47FixedPrecision::round(
48        DigitList &value, int32_t exponent, UErrorCode &status) const {
49    if (U_FAILURE(status)) {
50        return value;
51    }
52    value .fContext.status &= ~DEC_Inexact;
53    if (!fRoundingIncrement.isZero()) {
54        if (exponent == 0) {
55            value.quantize(fRoundingIncrement, status);
56        } else {
57            DigitList adjustedIncrement(fRoundingIncrement);
58            adjustedIncrement.shiftDecimalRight(exponent);
59            value.quantize(adjustedIncrement, status);
60        }
61        if (U_FAILURE(status)) {
62            return value;
63        }
64    }
65    int32_t leastSig = fMax.getLeastSignificantInclusive();
66    if (leastSig == INT32_MIN) {
67        value.round(fSignificant.getMax());
68    } else {
69        value.roundAtExponent(
70                exponent + leastSig,
71                fSignificant.getMax());
72    }
73    if (fExactOnly && (value.fContext.status & DEC_Inexact)) {
74        status = U_FORMAT_INEXACT_ERROR;
75    } else if (fFailIfOverMax) {
76        // Smallest interval for value stored in interval
77        DigitInterval interval;
78        value.getSmallestInterval(interval);
79        if (fMax.getIntDigitCount() < interval.getIntDigitCount()) {
80            status = U_ILLEGAL_ARGUMENT_ERROR;
81        }
82    }
83    return value;
84}
85
86DigitInterval &
87FixedPrecision::getIntervalForZero(DigitInterval &interval) const {
88    interval = fMin;
89    if (fSignificant.getMin() > 0) {
90        interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin());
91    }
92    interval.shrinkToFitWithin(fMax);
93    return interval;
94}
95
96DigitInterval &
97FixedPrecision::getInterval(
98        int32_t upperExponent, DigitInterval &interval) const {
99    if (fSignificant.getMin() > 0) {
100        interval.expandToContainDigit(
101                upperExponent - fSignificant.getMin());
102    }
103    interval.expandToContain(fMin);
104    interval.shrinkToFitWithin(fMax);
105    return interval;
106}
107
108DigitInterval &
109FixedPrecision::getInterval(
110        const DigitList &value, DigitInterval &interval) const {
111    if (value.isZero()) {
112        interval = fMin;
113        if (fSignificant.getMin() > 0) {
114            interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin());
115        }
116    } else {
117        value.getSmallestInterval(interval);
118        if (fSignificant.getMin() > 0) {
119            interval.expandToContainDigit(
120                    value.getUpperExponent() - fSignificant.getMin());
121        }
122        interval.expandToContain(fMin);
123    }
124    interval.shrinkToFitWithin(fMax);
125    return interval;
126}
127
128UBool
129FixedPrecision::isFastFormattable() const {
130    return (fMin.getFracDigitCount() == 0 && fSignificant.isNoConstraints() && fRoundingIncrement.isZero() && !fFailIfOverMax);
131}
132
133UBool
134FixedPrecision::handleNonNumeric(DigitList &value, VisibleDigits &digits) {
135    if (value.isNaN()) {
136        digits.setNaN();
137        return TRUE;
138    }
139    if (value.isInfinite()) {
140        digits.setInfinite();
141        if (!value.isPositive()) {
142            digits.setNegative();
143        }
144        return TRUE;
145    }
146    return FALSE;
147}
148
149VisibleDigits &
150FixedPrecision::initVisibleDigits(
151        DigitList &value,
152        VisibleDigits &digits,
153        UErrorCode &status) const {
154    if (U_FAILURE(status)) {
155        return digits;
156    }
157    digits.clear();
158    if (handleNonNumeric(value, digits)) {
159        return digits;
160    }
161    if (!value.isPositive()) {
162        digits.setNegative();
163    }
164    value.setRoundingMode(fRoundingMode);
165    round(value, 0, status);
166    getInterval(value, digits.fInterval);
167    digits.fExponent = value.getLowerExponent();
168    value.appendDigitsTo(digits.fDigits, status);
169    return digits;
170}
171
172VisibleDigits &
173FixedPrecision::initVisibleDigits(
174        int64_t value,
175        VisibleDigits &digits,
176        UErrorCode &status) const {
177    if (U_FAILURE(status)) {
178        return digits;
179    }
180    if (!fRoundingIncrement.isZero()) {
181        // If we have round increment, use digit list.
182        DigitList digitList;
183        digitList.set(value);
184        return initVisibleDigits(digitList, digits, status);
185    }
186    // Try fast path
187    if (initVisibleDigits(value, 0, digits, status)) {
188        digits.fAbsDoubleValue = fabs((double) value);
189        digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits();
190        return digits;
191    }
192    // Oops have to use digit list
193    DigitList digitList;
194    digitList.set(value);
195    return initVisibleDigits(digitList, digits, status);
196}
197
198VisibleDigits &
199FixedPrecision::initVisibleDigits(
200        double value,
201        VisibleDigits &digits,
202        UErrorCode &status) const {
203    if (U_FAILURE(status)) {
204        return digits;
205    }
206    digits.clear();
207    if (uprv_isNaN(value)) {
208        digits.setNaN();
209        return digits;
210    }
211    if (uprv_isPositiveInfinity(value)) {
212        digits.setInfinite();
213        return digits;
214    }
215    if (uprv_isNegativeInfinity(value)) {
216        digits.setInfinite();
217        digits.setNegative();
218        return digits;
219    }
220    if (!fRoundingIncrement.isZero()) {
221        // If we have round increment, use digit list.
222        DigitList digitList;
223        digitList.set(value);
224        return initVisibleDigits(digitList, digits, status);
225    }
226    // Try to find n such that value * 10^n is an integer
227    int32_t n = -1;
228    double scaled;
229    for (int32_t i = 0; i < UPRV_LENGTHOF(gPower10); ++i) {
230        scaled = value * gPower10[i];
231        if (scaled > MAX_INT64_IN_DOUBLE || scaled < -MAX_INT64_IN_DOUBLE) {
232            break;
233        }
234        if (scaled == floor(scaled)) {
235            n = i;
236            break;
237        }
238    }
239    // Try fast path
240    if (n >= 0 && initVisibleDigits(scaled, -n, digits, status)) {
241        digits.fAbsDoubleValue = fabs(value);
242        digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits();
243        // Adjust for negative 0 becuase when we cast to an int64,
244        // negative 0 becomes positive 0.
245        if (scaled == 0.0 && uprv_isNegative(scaled)) {
246            digits.setNegative();
247        }
248        return digits;
249    }
250
251    // Oops have to use digit list
252    DigitList digitList;
253    digitList.set(value);
254    return initVisibleDigits(digitList, digits, status);
255}
256
257UBool
258FixedPrecision::initVisibleDigits(
259        int64_t mantissa,
260        int32_t exponent,
261        VisibleDigits &digits,
262        UErrorCode &status) const {
263    if (U_FAILURE(status)) {
264        return TRUE;
265    }
266    digits.clear();
267
268    // Precompute fAbsIntValue if it is small enough, but we don't know yet
269    // if it will be valid.
270    UBool absIntValueComputed = FALSE;
271    if (mantissa > -1000000000000000000LL /* -1e18 */
272            && mantissa < 1000000000000000000LL /* 1e18 */) {
273        digits.fAbsIntValue = mantissa;
274        if (digits.fAbsIntValue < 0) {
275            digits.fAbsIntValue = -digits.fAbsIntValue;
276        }
277        int32_t i = 0;
278        int32_t maxPower10Exp = UPRV_LENGTHOF(gPower10) - 1;
279        for (; i > exponent + maxPower10Exp; i -= maxPower10Exp) {
280            digits.fAbsIntValue /= gPower10[maxPower10Exp];
281        }
282        digits.fAbsIntValue /= gPower10[i - exponent];
283        absIntValueComputed = TRUE;
284    }
285    if (mantissa == 0) {
286        getIntervalForZero(digits.fInterval);
287        digits.fAbsIntValueSet = absIntValueComputed;
288        return TRUE;
289    }
290    // be sure least significant digit is non zero
291    while (mantissa % 10 == 0) {
292        mantissa /= 10;
293        ++exponent;
294    }
295    if (mantissa < 0) {
296        digits.fDigits.append((char) -(mantissa % -10), status);
297        mantissa /= -10;
298        digits.setNegative();
299    }
300    while (mantissa) {
301        digits.fDigits.append((char) (mantissa % 10), status);
302        mantissa /= 10;
303    }
304    if (U_FAILURE(status)) {
305        return TRUE;
306    }
307    digits.fExponent = exponent;
308    int32_t upperExponent = exponent + digits.fDigits.length();
309    if (fFailIfOverMax && upperExponent > fMax.getIntDigitCount()) {
310        status = U_ILLEGAL_ARGUMENT_ERROR;
311        return TRUE;
312    }
313    UBool roundingRequired =
314            isRoundingRequired(upperExponent, exponent);
315    if (roundingRequired) {
316        if (fExactOnly) {
317            status = U_FORMAT_INEXACT_ERROR;
318            return TRUE;
319        }
320        return FALSE;
321    }
322    digits.fInterval.setLeastSignificantInclusive(exponent);
323    digits.fInterval.setMostSignificantExclusive(upperExponent);
324    getInterval(upperExponent, digits.fInterval);
325
326    // The intValue we computed above is only valid if our visible digits
327    // doesn't exceed the maximum integer digits allowed.
328    digits.fAbsIntValueSet = absIntValueComputed && !digits.isOverMaxDigits();
329    return TRUE;
330}
331
332VisibleDigitsWithExponent &
333FixedPrecision::initVisibleDigitsWithExponent(
334        DigitList &value,
335        VisibleDigitsWithExponent &digits,
336        UErrorCode &status) const {
337    digits.clear();
338    initVisibleDigits(value, digits.fMantissa, status);
339    return digits;
340}
341
342VisibleDigitsWithExponent &
343FixedPrecision::initVisibleDigitsWithExponent(
344        double value,
345        VisibleDigitsWithExponent &digits,
346        UErrorCode &status) const {
347    digits.clear();
348    initVisibleDigits(value, digits.fMantissa, status);
349    return digits;
350}
351
352VisibleDigitsWithExponent &
353FixedPrecision::initVisibleDigitsWithExponent(
354        int64_t value,
355        VisibleDigitsWithExponent &digits,
356        UErrorCode &status) const {
357    digits.clear();
358    initVisibleDigits(value, digits.fMantissa, status);
359    return digits;
360}
361
362ScientificPrecision::ScientificPrecision() : fMinExponentDigits(1) {
363}
364
365DigitList &
366ScientificPrecision::round(DigitList &value, UErrorCode &status) const {
367    if (U_FAILURE(status)) {
368        return value;
369    }
370    int32_t exponent = value.getScientificExponent(
371            fMantissa.fMin.getIntDigitCount(), getMultiplier());
372    return fMantissa.round(value, exponent, status);
373}
374
375int32_t
376ScientificPrecision::toScientific(DigitList &value) const {
377    return value.toScientific(
378            fMantissa.fMin.getIntDigitCount(), getMultiplier());
379}
380
381int32_t
382ScientificPrecision::getMultiplier() const {
383    int32_t maxIntDigitCount = fMantissa.fMax.getIntDigitCount();
384    if (maxIntDigitCount == INT32_MAX) {
385        return 1;
386    }
387    int32_t multiplier =
388        maxIntDigitCount - fMantissa.fMin.getIntDigitCount() + 1;
389    return (multiplier < 1 ? 1 : multiplier);
390}
391
392VisibleDigitsWithExponent &
393ScientificPrecision::initVisibleDigitsWithExponent(
394        DigitList &value,
395        VisibleDigitsWithExponent &digits,
396        UErrorCode &status) const {
397    if (U_FAILURE(status)) {
398        return digits;
399    }
400    digits.clear();
401    if (FixedPrecision::handleNonNumeric(value, digits.fMantissa)) {
402        return digits;
403    }
404    value.setRoundingMode(fMantissa.fRoundingMode);
405    int64_t exponent = toScientific(round(value, status));
406    fMantissa.initVisibleDigits(value, digits.fMantissa, status);
407    FixedPrecision exponentPrecision;
408    exponentPrecision.fMin.setIntDigitCount(fMinExponentDigits);
409    exponentPrecision.initVisibleDigits(exponent, digits.fExponent, status);
410    digits.fHasExponent = TRUE;
411    return digits;
412}
413
414VisibleDigitsWithExponent &
415ScientificPrecision::initVisibleDigitsWithExponent(
416        double value,
417        VisibleDigitsWithExponent &digits,
418        UErrorCode &status) const {
419    if (U_FAILURE(status)) {
420        return digits;
421    }
422    DigitList digitList;
423    digitList.set(value);
424    return initVisibleDigitsWithExponent(digitList, digits, status);
425}
426
427VisibleDigitsWithExponent &
428ScientificPrecision::initVisibleDigitsWithExponent(
429        int64_t value,
430        VisibleDigitsWithExponent &digits,
431        UErrorCode &status) const {
432    if (U_FAILURE(status)) {
433        return digits;
434    }
435    DigitList digitList;
436    digitList.set(value);
437    return initVisibleDigitsWithExponent(digitList, digits, status);
438}
439
440
441U_NAMESPACE_END
442#endif /* #if !UCONFIG_NO_FORMATTING */
443