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