diff options
Diffstat (limited to 'intl/icu/source/i18n/precision.cpp')
-rw-r--r-- | intl/icu/source/i18n/precision.cpp | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/precision.cpp b/intl/icu/source/i18n/precision.cpp new file mode 100644 index 000000000..086ce417f --- /dev/null +++ b/intl/icu/source/i18n/precision.cpp @@ -0,0 +1,444 @@ +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + * Copyright (C) 2015, International Business Machines + * Corporation and others. All Rights Reserved. + * + * file name: precisison.cpp + */ + +#include <math.h> + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "digitlst.h" +#include "fmtableimp.h" +#include "precision.h" +#include "putilimp.h" +#include "visibledigits.h" + +U_NAMESPACE_BEGIN + +static const int32_t gPower10[] = {1, 10, 100, 1000}; + +FixedPrecision::FixedPrecision() + : fExactOnly(FALSE), fFailIfOverMax(FALSE), fRoundingMode(DecimalFormat::kRoundHalfEven) { + fMin.setIntDigitCount(1); + fMin.setFracDigitCount(0); +} + +UBool +FixedPrecision::isRoundingRequired( + int32_t upperExponent, int32_t lowerExponent) const { + int32_t leastSigAllowed = fMax.getLeastSignificantInclusive(); + int32_t maxSignificantDigits = fSignificant.getMax(); + int32_t roundDigit; + if (maxSignificantDigits == INT32_MAX) { + roundDigit = leastSigAllowed; + } else { + int32_t limitDigit = upperExponent - maxSignificantDigits; + roundDigit = + limitDigit > leastSigAllowed ? limitDigit : leastSigAllowed; + } + return (roundDigit > lowerExponent); +} + +DigitList & +FixedPrecision::round( + DigitList &value, int32_t exponent, UErrorCode &status) const { + if (U_FAILURE(status)) { + return value; + } + value .fContext.status &= ~DEC_Inexact; + if (!fRoundingIncrement.isZero()) { + if (exponent == 0) { + value.quantize(fRoundingIncrement, status); + } else { + DigitList adjustedIncrement(fRoundingIncrement); + adjustedIncrement.shiftDecimalRight(exponent); + value.quantize(adjustedIncrement, status); + } + if (U_FAILURE(status)) { + return value; + } + } + int32_t leastSig = fMax.getLeastSignificantInclusive(); + if (leastSig == INT32_MIN) { + value.round(fSignificant.getMax()); + } else { + value.roundAtExponent( + exponent + leastSig, + fSignificant.getMax()); + } + if (fExactOnly && (value.fContext.status & DEC_Inexact)) { + status = U_FORMAT_INEXACT_ERROR; + } else if (fFailIfOverMax) { + // Smallest interval for value stored in interval + DigitInterval interval; + value.getSmallestInterval(interval); + if (fMax.getIntDigitCount() < interval.getIntDigitCount()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } + } + return value; +} + +DigitInterval & +FixedPrecision::getIntervalForZero(DigitInterval &interval) const { + interval = fMin; + if (fSignificant.getMin() > 0) { + interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin()); + } + interval.shrinkToFitWithin(fMax); + return interval; +} + +DigitInterval & +FixedPrecision::getInterval( + int32_t upperExponent, DigitInterval &interval) const { + if (fSignificant.getMin() > 0) { + interval.expandToContainDigit( + upperExponent - fSignificant.getMin()); + } + interval.expandToContain(fMin); + interval.shrinkToFitWithin(fMax); + return interval; +} + +DigitInterval & +FixedPrecision::getInterval( + const DigitList &value, DigitInterval &interval) const { + if (value.isZero()) { + interval = fMin; + if (fSignificant.getMin() > 0) { + interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin()); + } + } else { + value.getSmallestInterval(interval); + if (fSignificant.getMin() > 0) { + interval.expandToContainDigit( + value.getUpperExponent() - fSignificant.getMin()); + } + interval.expandToContain(fMin); + } + interval.shrinkToFitWithin(fMax); + return interval; +} + +UBool +FixedPrecision::isFastFormattable() const { + return (fMin.getFracDigitCount() == 0 && fSignificant.isNoConstraints() && fRoundingIncrement.isZero() && !fFailIfOverMax); +} + +UBool +FixedPrecision::handleNonNumeric(DigitList &value, VisibleDigits &digits) { + if (value.isNaN()) { + digits.setNaN(); + return TRUE; + } + if (value.isInfinite()) { + digits.setInfinite(); + if (!value.isPositive()) { + digits.setNegative(); + } + return TRUE; + } + return FALSE; +} + +VisibleDigits & +FixedPrecision::initVisibleDigits( + DigitList &value, + VisibleDigits &digits, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return digits; + } + digits.clear(); + if (handleNonNumeric(value, digits)) { + return digits; + } + if (!value.isPositive()) { + digits.setNegative(); + } + value.setRoundingMode(fRoundingMode); + round(value, 0, status); + getInterval(value, digits.fInterval); + digits.fExponent = value.getLowerExponent(); + value.appendDigitsTo(digits.fDigits, status); + return digits; +} + +VisibleDigits & +FixedPrecision::initVisibleDigits( + int64_t value, + VisibleDigits &digits, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return digits; + } + if (!fRoundingIncrement.isZero()) { + // If we have round increment, use digit list. + DigitList digitList; + digitList.set(value); + return initVisibleDigits(digitList, digits, status); + } + // Try fast path + if (initVisibleDigits(value, 0, digits, status)) { + digits.fAbsDoubleValue = fabs((double) value); + digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits(); + return digits; + } + // Oops have to use digit list + DigitList digitList; + digitList.set(value); + return initVisibleDigits(digitList, digits, status); +} + +VisibleDigits & +FixedPrecision::initVisibleDigits( + double value, + VisibleDigits &digits, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return digits; + } + digits.clear(); + if (uprv_isNaN(value)) { + digits.setNaN(); + return digits; + } + if (uprv_isPositiveInfinity(value)) { + digits.setInfinite(); + return digits; + } + if (uprv_isNegativeInfinity(value)) { + digits.setInfinite(); + digits.setNegative(); + return digits; + } + if (!fRoundingIncrement.isZero()) { + // If we have round increment, use digit list. + DigitList digitList; + digitList.set(value); + return initVisibleDigits(digitList, digits, status); + } + // Try to find n such that value * 10^n is an integer + int32_t n = -1; + double scaled; + for (int32_t i = 0; i < UPRV_LENGTHOF(gPower10); ++i) { + scaled = value * gPower10[i]; + if (scaled > MAX_INT64_IN_DOUBLE || scaled < -MAX_INT64_IN_DOUBLE) { + break; + } + if (scaled == floor(scaled)) { + n = i; + break; + } + } + // Try fast path + if (n >= 0 && initVisibleDigits(scaled, -n, digits, status)) { + digits.fAbsDoubleValue = fabs(value); + digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits(); + // Adjust for negative 0 becuase when we cast to an int64, + // negative 0 becomes positive 0. + if (scaled == 0.0 && uprv_isNegative(scaled)) { + digits.setNegative(); + } + return digits; + } + + // Oops have to use digit list + DigitList digitList; + digitList.set(value); + return initVisibleDigits(digitList, digits, status); +} + +UBool +FixedPrecision::initVisibleDigits( + int64_t mantissa, + int32_t exponent, + VisibleDigits &digits, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return TRUE; + } + digits.clear(); + + // Precompute fAbsIntValue if it is small enough, but we don't know yet + // if it will be valid. + UBool absIntValueComputed = FALSE; + if (mantissa > -1000000000000000000LL /* -1e18 */ + && mantissa < 1000000000000000000LL /* 1e18 */) { + digits.fAbsIntValue = mantissa; + if (digits.fAbsIntValue < 0) { + digits.fAbsIntValue = -digits.fAbsIntValue; + } + int32_t i = 0; + int32_t maxPower10Exp = UPRV_LENGTHOF(gPower10) - 1; + for (; i > exponent + maxPower10Exp; i -= maxPower10Exp) { + digits.fAbsIntValue /= gPower10[maxPower10Exp]; + } + digits.fAbsIntValue /= gPower10[i - exponent]; + absIntValueComputed = TRUE; + } + if (mantissa == 0) { + getIntervalForZero(digits.fInterval); + digits.fAbsIntValueSet = absIntValueComputed; + return TRUE; + } + // be sure least significant digit is non zero + while (mantissa % 10 == 0) { + mantissa /= 10; + ++exponent; + } + if (mantissa < 0) { + digits.fDigits.append((char) -(mantissa % -10), status); + mantissa /= -10; + digits.setNegative(); + } + while (mantissa) { + digits.fDigits.append((char) (mantissa % 10), status); + mantissa /= 10; + } + if (U_FAILURE(status)) { + return TRUE; + } + digits.fExponent = exponent; + int32_t upperExponent = exponent + digits.fDigits.length(); + if (fFailIfOverMax && upperExponent > fMax.getIntDigitCount()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return TRUE; + } + UBool roundingRequired = + isRoundingRequired(upperExponent, exponent); + if (roundingRequired) { + if (fExactOnly) { + status = U_FORMAT_INEXACT_ERROR; + return TRUE; + } + return FALSE; + } + digits.fInterval.setLeastSignificantInclusive(exponent); + digits.fInterval.setMostSignificantExclusive(upperExponent); + getInterval(upperExponent, digits.fInterval); + + // The intValue we computed above is only valid if our visible digits + // doesn't exceed the maximum integer digits allowed. + digits.fAbsIntValueSet = absIntValueComputed && !digits.isOverMaxDigits(); + return TRUE; +} + +VisibleDigitsWithExponent & +FixedPrecision::initVisibleDigitsWithExponent( + DigitList &value, + VisibleDigitsWithExponent &digits, + UErrorCode &status) const { + digits.clear(); + initVisibleDigits(value, digits.fMantissa, status); + return digits; +} + +VisibleDigitsWithExponent & +FixedPrecision::initVisibleDigitsWithExponent( + double value, + VisibleDigitsWithExponent &digits, + UErrorCode &status) const { + digits.clear(); + initVisibleDigits(value, digits.fMantissa, status); + return digits; +} + +VisibleDigitsWithExponent & +FixedPrecision::initVisibleDigitsWithExponent( + int64_t value, + VisibleDigitsWithExponent &digits, + UErrorCode &status) const { + digits.clear(); + initVisibleDigits(value, digits.fMantissa, status); + return digits; +} + +ScientificPrecision::ScientificPrecision() : fMinExponentDigits(1) { +} + +DigitList & +ScientificPrecision::round(DigitList &value, UErrorCode &status) const { + if (U_FAILURE(status)) { + return value; + } + int32_t exponent = value.getScientificExponent( + fMantissa.fMin.getIntDigitCount(), getMultiplier()); + return fMantissa.round(value, exponent, status); +} + +int32_t +ScientificPrecision::toScientific(DigitList &value) const { + return value.toScientific( + fMantissa.fMin.getIntDigitCount(), getMultiplier()); +} + +int32_t +ScientificPrecision::getMultiplier() const { + int32_t maxIntDigitCount = fMantissa.fMax.getIntDigitCount(); + if (maxIntDigitCount == INT32_MAX) { + return 1; + } + int32_t multiplier = + maxIntDigitCount - fMantissa.fMin.getIntDigitCount() + 1; + return (multiplier < 1 ? 1 : multiplier); +} + +VisibleDigitsWithExponent & +ScientificPrecision::initVisibleDigitsWithExponent( + DigitList &value, + VisibleDigitsWithExponent &digits, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return digits; + } + digits.clear(); + if (FixedPrecision::handleNonNumeric(value, digits.fMantissa)) { + return digits; + } + value.setRoundingMode(fMantissa.fRoundingMode); + int64_t exponent = toScientific(round(value, status)); + fMantissa.initVisibleDigits(value, digits.fMantissa, status); + FixedPrecision exponentPrecision; + exponentPrecision.fMin.setIntDigitCount(fMinExponentDigits); + exponentPrecision.initVisibleDigits(exponent, digits.fExponent, status); + digits.fHasExponent = TRUE; + return digits; +} + +VisibleDigitsWithExponent & +ScientificPrecision::initVisibleDigitsWithExponent( + double value, + VisibleDigitsWithExponent &digits, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return digits; + } + DigitList digitList; + digitList.set(value); + return initVisibleDigitsWithExponent(digitList, digits, status); +} + +VisibleDigitsWithExponent & +ScientificPrecision::initVisibleDigitsWithExponent( + int64_t value, + VisibleDigitsWithExponent &digits, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return digits; + } + DigitList digitList; + digitList.set(value); + return initVisibleDigitsWithExponent(digitList, digits, status); +} + + +U_NAMESPACE_END +#endif /* #if !UCONFIG_NO_FORMATTING */ |