1 // Copyright (C) 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 26 FixedPrecision::FixedPrecision() 27 : fExactOnly(FALSE), fFailIfOverMax(FALSE), fRoundingMode(DecimalFormat::kRoundHalfEven) { 28 fMin.setIntDigitCount(1); 29 fMin.setFracDigitCount(0); 30 } 31 32 UBool 33 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 & 49 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 & 89 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 & 99 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 & 111 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 131 FixedPrecision::isFastFormattable() const { 132 return (fMin.getFracDigitCount() == 0 && fSignificant.isNoConstraints() && fRoundingIncrement.isZero() && !fFailIfOverMax); 133 } 134 135 UBool 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 & 152 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 & 175 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 & 201 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(scaled, -n, digits, status)) { 243 digits.fAbsDoubleValue = fabs(value); 244 digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits(); 245 // Adjust for negative 0 becuase 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 260 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 & 335 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 & 345 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 & 355 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 364 ScientificPrecision::ScientificPrecision() : fMinExponentDigits(1) { 365 } 366 367 DigitList & 368 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 378 ScientificPrecision::toScientific(DigitList &value) const { 379 return value.toScientific( 380 fMantissa.fMin.getIntDigitCount(), getMultiplier()); 381 } 382 383 int32_t 384 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 & 395 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 & 417 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 & 430 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