]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/precision.cpp
ICU-57131.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / precision.cpp
CommitLineData
2ca993e8
A
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 */