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 
20 U_NAMESPACE_BEGIN
21 
22 static const int32_t gPower10[] = {1, 10, 100, 1000};
23 
FixedPrecision()24 FixedPrecision::FixedPrecision()
25         : fExactOnly(FALSE), fFailIfOverMax(FALSE), fRoundingMode(DecimalFormat::kRoundHalfEven) {
26     fMin.setIntDigitCount(1);
27     fMin.setFracDigitCount(0);
28 }
29 
30 UBool
isRoundingRequired(int32_t upperExponent,int32_t lowerExponent) const31 FixedPrecision::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 
46 DigitList &
round(DigitList & value,int32_t exponent,UErrorCode & status) const47 FixedPrecision::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 
86 DigitInterval &
getIntervalForZero(DigitInterval & interval) const87 FixedPrecision::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 
96 DigitInterval &
getInterval(int32_t upperExponent,DigitInterval & interval) const97 FixedPrecision::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 
108 DigitInterval &
getInterval(const DigitList & value,DigitInterval & interval) const109 FixedPrecision::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 
128 UBool
isFastFormattable() const129 FixedPrecision::isFastFormattable() const {
130     return (fMin.getFracDigitCount() == 0 && fSignificant.isNoConstraints() && fRoundingIncrement.isZero() && !fFailIfOverMax);
131 }
132 
133 UBool
handleNonNumeric(DigitList & value,VisibleDigits & digits)134 FixedPrecision::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 
149 VisibleDigits &
initVisibleDigits(DigitList & value,VisibleDigits & digits,UErrorCode & status) const150 FixedPrecision::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 
172 VisibleDigits &
initVisibleDigits(int64_t value,VisibleDigits & digits,UErrorCode & status) const173 FixedPrecision::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 
198 VisibleDigits &
initVisibleDigits(double value,VisibleDigits & digits,UErrorCode & status) const199 FixedPrecision::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 
257 UBool
initVisibleDigits(int64_t mantissa,int32_t exponent,VisibleDigits & digits,UErrorCode & status) const258 FixedPrecision::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 
332 VisibleDigitsWithExponent &
initVisibleDigitsWithExponent(DigitList & value,VisibleDigitsWithExponent & digits,UErrorCode & status) const333 FixedPrecision::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 
342 VisibleDigitsWithExponent &
initVisibleDigitsWithExponent(double value,VisibleDigitsWithExponent & digits,UErrorCode & status) const343 FixedPrecision::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 
352 VisibleDigitsWithExponent &
initVisibleDigitsWithExponent(int64_t value,VisibleDigitsWithExponent & digits,UErrorCode & status) const353 FixedPrecision::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 
ScientificPrecision()362 ScientificPrecision::ScientificPrecision() : fMinExponentDigits(1) {
363 }
364 
365 DigitList &
round(DigitList & value,UErrorCode & status) const366 ScientificPrecision::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 
375 int32_t
toScientific(DigitList & value) const376 ScientificPrecision::toScientific(DigitList &value) const {
377     return value.toScientific(
378             fMantissa.fMin.getIntDigitCount(), getMultiplier());
379 }
380 
381 int32_t
getMultiplier() const382 ScientificPrecision::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 
392 VisibleDigitsWithExponent &
initVisibleDigitsWithExponent(DigitList & value,VisibleDigitsWithExponent & digits,UErrorCode & status) const393 ScientificPrecision::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 
414 VisibleDigitsWithExponent &
initVisibleDigitsWithExponent(double value,VisibleDigitsWithExponent & digits,UErrorCode & status) const415 ScientificPrecision::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 
427 VisibleDigitsWithExponent &
initVisibleDigitsWithExponent(int64_t value,VisibleDigitsWithExponent & digits,UErrorCode & status) const428 ScientificPrecision::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 
441 U_NAMESPACE_END
442 #endif /* #if !UCONFIG_NO_FORMATTING */
443