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