summaryrefslogtreecommitdiff
path: root/deps/icu-small/source/i18n/number_rounding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'deps/icu-small/source/i18n/number_rounding.cpp')
-rw-r--r--deps/icu-small/source/i18n/number_rounding.cpp195
1 files changed, 125 insertions, 70 deletions
diff --git a/deps/icu-small/source/i18n/number_rounding.cpp b/deps/icu-small/source/i18n/number_rounding.cpp
index fd4dafdf98..ae4b8849fb 100644
--- a/deps/icu-small/source/i18n/number_rounding.cpp
+++ b/deps/icu-small/source/i18n/number_rounding.cpp
@@ -3,17 +3,23 @@
#include "unicode/utypes.h"
-#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
+#if !UCONFIG_NO_FORMATTING
#include "uassert.h"
#include "unicode/numberformatter.h"
#include "number_types.h"
#include "number_decimalquantity.h"
+#include "double-conversion.h"
+#include "number_roundingutils.h"
+#include "putilimp.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
+
+using double_conversion::DoubleToStringConverter;
+
namespace {
int32_t getRoundingMagnitudeFraction(int maxFrac) {
@@ -46,15 +52,38 @@ int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig)
}
-Rounder Rounder::unlimited() {
- return Rounder(RND_NONE, {}, kDefaultMode);
+MultiplierProducer::~MultiplierProducer() = default;
+
+
+digits_t roundingutils::doubleFractionLength(double input) {
+ char buffer[DoubleToStringConverter::kBase10MaximalLength + 1];
+ bool sign; // unused; always positive
+ int32_t length;
+ int32_t point;
+ DoubleToStringConverter::DoubleToAscii(
+ input,
+ DoubleToStringConverter::DtoaMode::SHORTEST,
+ 0,
+ buffer,
+ sizeof(buffer),
+ &sign,
+ &length,
+ &point
+ );
+
+ return static_cast<digits_t>(length - point);
}
-FractionRounder Rounder::integer() {
+
+Precision Precision::unlimited() {
+ return Precision(RND_NONE, {}, kDefaultMode);
+}
+
+FractionPrecision Precision::integer() {
return constructFraction(0, 0);
}
-FractionRounder Rounder::fixedFraction(int32_t minMaxFractionPlaces) {
+FractionPrecision Precision::fixedFraction(int32_t minMaxFractionPlaces) {
if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) {
return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
} else {
@@ -62,7 +91,7 @@ FractionRounder Rounder::fixedFraction(int32_t minMaxFractionPlaces) {
}
}
-FractionRounder Rounder::minFraction(int32_t minFractionPlaces) {
+FractionPrecision Precision::minFraction(int32_t minFractionPlaces) {
if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) {
return constructFraction(minFractionPlaces, -1);
} else {
@@ -70,7 +99,7 @@ FractionRounder Rounder::minFraction(int32_t minFractionPlaces) {
}
}
-FractionRounder Rounder::maxFraction(int32_t maxFractionPlaces) {
+FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) {
if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) {
return constructFraction(0, maxFractionPlaces);
} else {
@@ -78,7 +107,7 @@ FractionRounder Rounder::maxFraction(int32_t maxFractionPlaces) {
}
}
-FractionRounder Rounder::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
+FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig &&
minFractionPlaces <= maxFractionPlaces) {
return constructFraction(minFractionPlaces, maxFractionPlaces);
@@ -87,7 +116,7 @@ FractionRounder Rounder::minMaxFraction(int32_t minFractionPlaces, int32_t maxFr
}
}
-Rounder Rounder::fixedDigits(int32_t minMaxSignificantDigits) {
+Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) {
if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) {
return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
} else {
@@ -95,7 +124,7 @@ Rounder Rounder::fixedDigits(int32_t minMaxSignificantDigits) {
}
}
-Rounder Rounder::minDigits(int32_t minSignificantDigits) {
+Precision Precision::minSignificantDigits(int32_t minSignificantDigits) {
if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
return constructSignificant(minSignificantDigits, -1);
} else {
@@ -103,7 +132,7 @@ Rounder Rounder::minDigits(int32_t minSignificantDigits) {
}
}
-Rounder Rounder::maxDigits(int32_t maxSignificantDigits) {
+Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) {
if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
return constructSignificant(1, maxSignificantDigits);
} else {
@@ -111,7 +140,7 @@ Rounder Rounder::maxDigits(int32_t maxSignificantDigits) {
}
}
-Rounder Rounder::minMaxDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
+Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig &&
minSignificantDigits <= maxSignificantDigits) {
return constructSignificant(minSignificantDigits, maxSignificantDigits);
@@ -120,7 +149,7 @@ Rounder Rounder::minMaxDigits(int32_t minSignificantDigits, int32_t maxSignifica
}
}
-IncrementRounder Rounder::increment(double roundingIncrement) {
+IncrementPrecision Precision::increment(double roundingIncrement) {
if (roundingIncrement > 0.0) {
return constructIncrement(roundingIncrement, 0);
} else {
@@ -128,16 +157,18 @@ IncrementRounder Rounder::increment(double roundingIncrement) {
}
}
-CurrencyRounder Rounder::currency(UCurrencyUsage currencyUsage) {
+CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) {
return constructCurrency(currencyUsage);
}
-Rounder Rounder::withMode(RoundingMode roundingMode) const {
+Precision Precision::withMode(RoundingMode roundingMode) const {
if (fType == RND_ERROR) { return *this; } // no-op in error state
- return {fType, fUnion, roundingMode};
+ Precision retval = *this;
+ retval.fRoundingMode = roundingMode;
+ return retval;
}
-Rounder FractionRounder::withMinDigits(int32_t minSignificantDigits) const {
+Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const {
if (fType == RND_ERROR) { return *this; } // no-op in error state
if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
return constructFractionSignificant(*this, minSignificantDigits, -1);
@@ -146,7 +177,7 @@ Rounder FractionRounder::withMinDigits(int32_t minSignificantDigits) const {
}
}
-Rounder FractionRounder::withMaxDigits(int32_t maxSignificantDigits) const {
+Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const {
if (fType == RND_ERROR) { return *this; } // no-op in error state
if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
return constructFractionSignificant(*this, -1, maxSignificantDigits);
@@ -156,7 +187,7 @@ Rounder FractionRounder::withMaxDigits(int32_t maxSignificantDigits) const {
}
// Private method on base class
-Rounder Rounder::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
+Precision Precision::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
if (fType == RND_ERROR) { return *this; } // no-op in error state
U_ASSERT(fType == RND_CURRENCY);
const char16_t *isoCode = currency.getISOCurrency();
@@ -170,17 +201,17 @@ Rounder Rounder::withCurrency(const CurrencyUnit &currency, UErrorCode &status)
}
}
-// Public method on CurrencyRounder subclass
-Rounder CurrencyRounder::withCurrency(const CurrencyUnit &currency) const {
+// Public method on CurrencyPrecision subclass
+Precision CurrencyPrecision::withCurrency(const CurrencyUnit &currency) const {
UErrorCode localStatus = U_ZERO_ERROR;
- Rounder result = Rounder::withCurrency(currency, localStatus);
+ Precision result = Precision::withCurrency(currency, localStatus);
if (U_FAILURE(localStatus)) {
return {localStatus};
}
return result;
}
-Rounder IncrementRounder::withMinFraction(int32_t minFrac) const {
+Precision IncrementPrecision::withMinFraction(int32_t minFrac) const {
if (fType == RND_ERROR) { return *this; } // no-op in error state
if (minFrac >= 0 && minFrac <= kMaxIntFracSig) {
return constructIncrement(fUnion.increment.fIncrement, minFrac);
@@ -189,67 +220,77 @@ Rounder IncrementRounder::withMinFraction(int32_t minFrac) const {
}
}
-FractionRounder Rounder::constructFraction(int32_t minFrac, int32_t maxFrac) {
+FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) {
FractionSignificantSettings settings;
settings.fMinFrac = static_cast<digits_t>(minFrac);
settings.fMaxFrac = static_cast<digits_t>(maxFrac);
settings.fMinSig = -1;
settings.fMaxSig = -1;
- RounderUnion union_;
+ PrecisionUnion union_;
union_.fracSig = settings;
return {RND_FRACTION, union_, kDefaultMode};
}
-Rounder Rounder::constructSignificant(int32_t minSig, int32_t maxSig) {
+Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
FractionSignificantSettings settings;
settings.fMinFrac = -1;
settings.fMaxFrac = -1;
settings.fMinSig = static_cast<digits_t>(minSig);
settings.fMaxSig = static_cast<digits_t>(maxSig);
- RounderUnion union_;
+ PrecisionUnion union_;
union_.fracSig = settings;
return {RND_SIGNIFICANT, union_, kDefaultMode};
}
-Rounder
-Rounder::constructFractionSignificant(const FractionRounder &base, int32_t minSig, int32_t maxSig) {
+Precision
+Precision::constructFractionSignificant(const FractionPrecision &base, int32_t minSig, int32_t maxSig) {
FractionSignificantSettings settings = base.fUnion.fracSig;
settings.fMinSig = static_cast<digits_t>(minSig);
settings.fMaxSig = static_cast<digits_t>(maxSig);
- RounderUnion union_;
+ PrecisionUnion union_;
union_.fracSig = settings;
return {RND_FRACTION_SIGNIFICANT, union_, kDefaultMode};
}
-IncrementRounder Rounder::constructIncrement(double increment, int32_t minFrac) {
+IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) {
IncrementSettings settings;
settings.fIncrement = increment;
settings.fMinFrac = static_cast<digits_t>(minFrac);
- RounderUnion union_;
+ // One of the few pre-computed quantities:
+ // Note: it is possible for minFrac to be more than maxFrac... (misleading)
+ settings.fMaxFrac = roundingutils::doubleFractionLength(increment);
+ PrecisionUnion union_;
union_.increment = settings;
return {RND_INCREMENT, union_, kDefaultMode};
}
-CurrencyRounder Rounder::constructCurrency(UCurrencyUsage usage) {
- RounderUnion union_;
+CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
+ PrecisionUnion union_;
union_.currencyUsage = usage;
return {RND_CURRENCY, union_, kDefaultMode};
}
-Rounder Rounder::constructPassThrough() {
- RounderUnion union_;
- union_.errorCode = U_ZERO_ERROR; // initialize the variable
- return {RND_PASS_THROUGH, union_, kDefaultMode};
-}
-void Rounder::setLocaleData(const CurrencyUnit &currency, UErrorCode &status) {
- if (fType == RND_CURRENCY) {
- *this = withCurrency(currency, status);
+RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
+ const CurrencyUnit& currency, UErrorCode& status)
+ : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) {
+ if (precision.fType == Precision::RND_CURRENCY) {
+ fPrecision = precision.withCurrency(currency, status);
}
}
+RoundingImpl RoundingImpl::passThrough() {
+ RoundingImpl retval;
+ retval.fPassThrough = true;
+ return retval;
+}
+
+bool RoundingImpl::isSignificantDigits() const {
+ return fPrecision.fType == Precision::RND_SIGNIFICANT;
+}
+
int32_t
-Rounder::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
+RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
UErrorCode &status) {
// Do not call this method with zero.
U_ASSERT(!input.isZero());
@@ -287,45 +328,59 @@ Rounder::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::Mult
}
/** This is the method that contains the actual rounding logic. */
-void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
- switch (fType) {
- case RND_BOGUS:
- case RND_ERROR:
+void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
+ if (fPassThrough) {
+ return;
+ }
+ switch (fPrecision.fType) {
+ case Precision::RND_BOGUS:
+ case Precision::RND_ERROR:
// Errors should be caught before the apply() method is called
status = U_INTERNAL_PROGRAM_ERROR;
break;
- case RND_NONE:
+ case Precision::RND_NONE:
value.roundToInfinity();
break;
- case RND_FRACTION:
+ case Precision::RND_FRACTION:
value.roundToMagnitude(
- getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac), fRoundingMode, status);
+ getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac),
+ fRoundingMode,
+ status);
value.setFractionLength(
- uprv_max(0, -getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac)), INT32_MAX);
+ uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)),
+ INT32_MAX);
break;
- case RND_SIGNIFICANT:
+ case Precision::RND_SIGNIFICANT:
value.roundToMagnitude(
- getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig),
+ getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig),
fRoundingMode,
status);
value.setFractionLength(
- uprv_max(0, -getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig)),
+ uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)),
INT32_MAX);
+ // Make sure that digits are displayed on zero.
+ if (value.isZero() && fPrecision.fUnion.fracSig.fMinSig > 0) {
+ value.setIntegerLength(1, INT32_MAX);
+ }
break;
- case RND_FRACTION_SIGNIFICANT: {
- int32_t displayMag = getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac);
- int32_t roundingMag = getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac);
- if (fUnion.fracSig.fMinSig == -1) {
+ case Precision::RND_FRACTION_SIGNIFICANT: {
+ int32_t displayMag = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac);
+ int32_t roundingMag = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac);
+ if (fPrecision.fUnion.fracSig.fMinSig == -1) {
// Max Sig override
- int32_t candidate = getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig);
+ int32_t candidate = getRoundingMagnitudeSignificant(
+ value,
+ fPrecision.fUnion.fracSig.fMaxSig);
roundingMag = uprv_max(roundingMag, candidate);
} else {
// Min Sig override
- int32_t candidate = getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig);
+ int32_t candidate = getDisplayMagnitudeSignificant(
+ value,
+ fPrecision.fUnion.fracSig.fMinSig);
roundingMag = uprv_min(roundingMag, candidate);
}
value.roundToMagnitude(roundingMag, fRoundingMode, status);
@@ -333,27 +388,27 @@ void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
break;
}
- case RND_INCREMENT:
+ case Precision::RND_INCREMENT:
value.roundToIncrement(
- fUnion.increment.fIncrement, fRoundingMode, fUnion.increment.fMinFrac, status);
- value.setFractionLength(fUnion.increment.fMinFrac, fUnion.increment.fMinFrac);
+ fPrecision.fUnion.increment.fIncrement,
+ fRoundingMode,
+ fPrecision.fUnion.increment.fMaxFrac,
+ status);
+ value.setFractionLength(fPrecision.fUnion.increment.fMinFrac, INT32_MAX);
break;
- case RND_CURRENCY:
+ case Precision::RND_CURRENCY:
// Call .withCurrency() before .apply()!
U_ASSERT(false);
break;
-
- case RND_PASS_THROUGH:
- break;
}
}
-void Rounder::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
+void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
// This method is intended for the one specific purpose of helping print "00.000E0".
- U_ASSERT(fType == RND_SIGNIFICANT);
+ U_ASSERT(isSignificantDigits());
U_ASSERT(value.isZero());
- value.setFractionLength(fUnion.fracSig.fMinSig - minInt, INT32_MAX);
+ value.setFractionLength(fPrecision.fUnion.fracSig.fMinSig - minInt, INT32_MAX);
}
#endif /* #if !UCONFIG_NO_FORMATTING */