| /* |
| * 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 */ |