summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/precision.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu/source/i18n/precision.cpp')
-rw-r--r--intl/icu/source/i18n/precision.cpp444
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 */