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