number_decimfmtprops.o number_fluent.o number_formatimpl.o number_grouping.o \
number_integerwidth.o number_longnames.o number_modifiers.o number_notation.o \
number_padding.o number_patternmodifier.o number_patternstring.o \
-number_rounding.o number_scientific.o number_stringbuilder.o \
+number_rounding.o number_scientific.o number_stringbuilder.o number_utils.o \
number_mapper.o number_multiplier.o number_currencysymbols.o number_skeletons.o number_capi.o \
double-conversion.o double-conversion-bignum-dtoa.o double-conversion-bignum.o \
double-conversion-cached-powers.o double-conversion-diy-fp.o \
#include "charstr.h"
#include "cmemory.h"
#include "cstring.h"
-#include "decNumber.h"
#include "fmtableimp.h"
#include "number_decimalquantity.h"
status = U_INVALID_FORMAT_ERROR;
return U_INT64_MIN;
} else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalQuantity != NULL) {
- int64_t val = fDecimalQuantity->toLong();
- if (val != 0) {
- return val;
+ if (fDecimalQuantity->fitsInLong()) {
+ return fDecimalQuantity->toLong();
+ } else if (fDecimalQuantity->isNegative()) {
+ return U_INT64_MIN;
} else {
- status = U_INVALID_FORMAT_ERROR;
- return fValue.fDouble > 0 ? U_INT64_MAX : U_INT64_MIN;
+ return U_INT64_MAX;
}
} else {
return (int64_t)fValue.fDouble;
#include "nfsubs.h"
#include "fmtableimp.h"
+#include "putilimp.h"
#include "number_decimalquantity.h"
#if U_HAVE_RBNF
#include "uassert.h"
#include <cmath>
#include "cmemory.h"
-#include "decNumber.h"
#include <limits>
+#include "putilimp.h"
#include "number_decimalquantity.h"
-#include "decContext.h"
-#include "decNumber.h"
#include "number_roundingutils.h"
#include "double-conversion.h"
#include "unicode/plurrule.h"
#include "charstr.h"
+#include "number_utils.h"
using namespace icu;
using namespace icu::number;
int8_t INFINITY_FLAG = 2;
int8_t NAN_FLAG = 4;
-static constexpr int32_t DEFAULT_DIGITS = 34;
-typedef MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS> DecNumberWithStorage;
-
-/** Helper function to convert a decNumber-compatible string into a decNumber. */
-void stringToDecNumber(StringPiece n, DecNumberWithStorage &dn, UErrorCode& status) {
- decContext set;
- uprv_decContextDefault(&set, DEC_INIT_BASE);
- uprv_decContextSetRounding(&set, DEC_ROUND_HALF_EVEN);
- set.traps = 0; // no traps, thank you (what does this mean?)
- if (n.length() > DEFAULT_DIGITS) {
- dn.resize(n.length(), 0);
- set.digits = n.length();
- } else {
- set.digits = DEFAULT_DIGITS;
- }
-
- // Make sure that the string is NUL-terminated; CharString guarantees this, but not StringPiece.
- CharString cs(n, status);
- if (U_FAILURE(status)) { return; }
-
- static_assert(DECDPUN == 1, "Assumes that DECDPUN is set to 1");
- uprv_decNumberFromString(dn.getAlias(), cs.data(), &set);
-
- // Check for invalid syntax and set the corresponding error code.
- if ((set.status & DEC_Conversion_syntax) != 0) {
- status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
- } else if (set.status != 0) {
- // Not a syntax error, but some other error, like an exponent that is too large.
- status = U_UNSUPPORTED_ERROR;
- }
-}
-
/** Helper function for safe subtraction (no overflow). */
inline int32_t safeSubtract(int32_t a, int32_t b) {
// Note: In C++, signed integer subtraction is undefined behavior.
roundToMagnitude(-maxFrac, roundingMode, status);
}
-void DecimalQuantity::multiplyBy(double multiplicand) {
+void DecimalQuantity::multiplyBy(const DecNum& multiplicand, UErrorCode& status) {
if (isInfinite() || isZero() || isNaN()) {
return;
}
- // Cover a few simple cases...
- if (multiplicand == 1) {
- return;
- } else if (multiplicand == -1) {
- negate();
+ // Convert to DecNum, multiply, and convert back.
+ DecNum decnum;
+ toDecNum(decnum, status);
+ if (U_FAILURE(status)) { return; }
+ decnum.multiplyBy(multiplicand, status);
+ if (U_FAILURE(status)) { return; }
+ setToDecNum(decnum, status);
+}
+
+void DecimalQuantity::divideBy(const DecNum& divisor, UErrorCode& status) {
+ if (isInfinite() || isZero() || isNaN()) {
return;
}
- // Do math for all other cases...
- // TODO: Should we convert to decNumber instead?
- double temp = toDouble();
- temp *= multiplicand;
- setToDouble(temp);
+ // Convert to DecNum, multiply, and convert back.
+ DecNum decnum;
+ toDecNum(decnum, status);
+ if (U_FAILURE(status)) { return; }
+ decnum.divideBy(divisor, status);
+ if (U_FAILURE(status)) { return; }
+ setToDecNum(decnum, status);
}
void DecimalQuantity::negate() {
DecimalQuantity &DecimalQuantity::setToLong(int64_t n) {
setBcdToZero();
flags = 0;
- if (n < 0) {
+ if (n < 0 && n > INT64_MIN) {
flags |= NEGATIVE_FLAG;
n = -n;
}
void DecimalQuantity::_setToLong(int64_t n) {
if (n == INT64_MIN) {
- static const char *int64minStr = "9.223372036854775808E+18";
- DecNumberWithStorage dn;
+ DecNum decnum;
UErrorCode localStatus = U_ZERO_ERROR;
- stringToDecNumber(int64minStr, dn, localStatus);
+ decnum.setTo("9.223372036854775808E+18", localStatus);
if (U_FAILURE(localStatus)) { return; } // unexpected
- readDecNumberToBcd(dn.getAlias());
+ flags |= NEGATIVE_FLAG;
+ readDecNumberToBcd(decnum);
} else if (n <= INT32_MAX) {
readIntToBcd(static_cast<int32_t>(n));
} else {
setBcdToZero();
flags = 0;
- DecNumberWithStorage dn;
- stringToDecNumber(n, dn, status);
- if (U_FAILURE(status)) { return *this; }
+ // Compute the decNumber representation
+ DecNum decnum;
+ decnum.setTo(n, status);
- // The code path for decNumber is modeled after BigDecimal in Java.
- if (decNumberIsNegative(dn.getAlias())) {
- flags |= NEGATIVE_FLAG;
- }
- if (!decNumberIsZero(dn.getAlias())) {
- _setToDecNumber(dn.getAlias());
- }
+ _setToDecNum(decnum, status);
return *this;
}
-void DecimalQuantity::_setToDecNumber(decNumber *n) {
- // Java fastpaths for ints here. In C++, just always read directly from the decNumber.
- readDecNumberToBcd(n);
- compact();
+DecimalQuantity& DecimalQuantity::setToDecNum(const DecNum& decnum, UErrorCode& status) {
+ setBcdToZero();
+ flags = 0;
+
+ _setToDecNum(decnum, status);
+ return *this;
+}
+
+void DecimalQuantity::_setToDecNum(const DecNum& decnum, UErrorCode& status) {
+ if (U_FAILURE(status)) { return; }
+ if (decnum.isNegative()) {
+ flags |= NEGATIVE_FLAG;
+ }
+ if (!decnum.isZero()) {
+ readDecNumberToBcd(decnum);
+ compact();
+ }
}
int64_t DecimalQuantity::toLong() const {
+ // NOTE: Call sites should be guarded by fitsInLong(), like this:
+ // if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ }
int64_t result = 0L;
for (int32_t magnitude = scale + precision - 1; magnitude >= 0; magnitude--) {
result = result * 10 + getDigitPos(magnitude - scale);
return result;
}
+void DecimalQuantity::toDecNum(DecNum& output, UErrorCode& status) const {
+ // Special handling for zero
+ if (precision == 0) {
+ output.setTo("0", status);
+ }
+
+ // Use the BCD constructor. We need to do a little bit of work to convert, though.
+ // The decNumber constructor expects most-significant first, but we store least-significant first.
+ MaybeStackArray<uint8_t, 20> ubcd(precision);
+ for (int32_t m = 0; m < precision; m++) {
+ ubcd[precision - m - 1] = static_cast<uint8_t>(getDigitPos(m));
+ }
+ output.setTo(ubcd.getAlias(), precision, scale, isNegative(), status);
+}
+
void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) {
// The position in the BCD at which rounding will be performed; digits to the right of position
// will be rounded away.
}
}
-void DecimalQuantity::readDecNumberToBcd(decNumber *dn) {
+void DecimalQuantity::readDecNumberToBcd(const DecNum& decnum) {
+ const decNumber* dn = decnum.getRawDecNumber();
if (dn->digits > 16) {
ensureCapacity(dn->digits);
for (int32_t i = 0; i < dn->digits; i++) {
#include <cstdint>
#include "unicode/umachine.h"
-#include "decNumber.h"
#include "standardplural.h"
#include "plurrule_impl.h"
#include "number_types.h"
U_NAMESPACE_BEGIN namespace number {
namespace impl {
+// Forward-declare (maybe don't want number_utils.h included here):
+class DecNum;
+
/**
* An class for representing a number to be processed by the decimal formatting pipeline. Includes
* methods for rounding, plural rules, and decimal digit extraction.
void roundToInfinity();
/**
- * Multiply the internal value.
+ * Multiply the internal value. Uses decNumber.
*
* @param multiplicand The value by which to multiply.
*/
- void multiplyBy(double multiplicand);
+ void multiplyBy(const DecNum& multiplicand, UErrorCode& status);
+
+ /**
+ * Divide the internal value. Uses decNumber.
+ *
+ * @param multiplicand The value by which to multiply.
+ */
+ void divideBy(const DecNum& divisor, UErrorCode& status);
/** Flips the sign from positive to negative and back. */
void negate();
/** @return The value contained in this {@link DecimalQuantity} approximated as a double. */
double toDouble() const;
+ /** Computes a DecNum representation of this DecimalQuantity, saving it to the output parameter. */
+ void toDecNum(DecNum& output, UErrorCode& status) const;
+
DecimalQuantity &setToInt(int32_t n);
DecimalQuantity &setToLong(int64_t n);
DecimalQuantity &setToDouble(double n);
/** decNumber is similar to BigDecimal in Java. */
-
DecimalQuantity &setToDecNumber(StringPiece n, UErrorCode& status);
+ /** Internal method if the caller already has a DecNum. */
+ DecimalQuantity &setToDecNum(const DecNum& n, UErrorCode& status);
+
/**
* Appends a digit, optionally with one or more leading zeros, to the end of the value represented
* by this DecimalQuantity.
*/
void readLongToBcd(int64_t n);
- void readDecNumberToBcd(decNumber *dn);
+ void readDecNumberToBcd(const DecNum& dn);
void readDoubleConversionToBcd(const char* buffer, int32_t length, int32_t point);
void _setToDoubleFast(double n);
- void _setToDecNumber(decNumber *n);
+ void _setToDecNum(const DecNum& dn, UErrorCode& status);
void convertToAccurateDouble();
template<typename Derived>
UnicodeString NumberFormatterSettings<Derived>::toSkeleton(UErrorCode& status) const {
+ if (fMacros.copyErrorTo(status)) {
+ return {};
+ }
return skeleton::generate(fMacros, status);
}
#include "number_types.h"
#include "number_multiplier.h"
#include "numparse_validators.h"
+#include "number_utils.h"
+#include "decNumber.h"
using namespace icu;
using namespace icu::number;
using namespace icu::numparse::impl;
-Multiplier::Multiplier(int32_t magnitude, double arbitrary)
- : fMagnitude(magnitude), fArbitrary(arbitrary) {}
+Multiplier::Multiplier(int32_t magnitude, DecNum* arbitraryToAdopt)
+ : fMagnitude(magnitude), fArbitrary(arbitraryToAdopt), fError(U_ZERO_ERROR) {
+ if (fArbitrary != nullptr) {
+ // Attempt to convert the DecNum to a magnitude multiplier.
+ fArbitrary->normalize();
+ if (fArbitrary->getRawDecNumber()->digits == 1 && fArbitrary->getRawDecNumber()->lsu[0] == 1 &&
+ !fArbitrary->isNegative()) {
+ // Success!
+ fMagnitude = fArbitrary->getRawDecNumber()->exponent;
+ delete fArbitrary;
+ fArbitrary = nullptr;
+ }
+ }
+}
+
+Multiplier::Multiplier(const Multiplier& other)
+ : fMagnitude(other.fMagnitude), fArbitrary(nullptr), fError(other.fError) {
+ if (other.fArbitrary != nullptr) {
+ UErrorCode localStatus = U_ZERO_ERROR;
+ fArbitrary = new DecNum(*other.fArbitrary, localStatus);
+ }
+}
+
+Multiplier& Multiplier::operator=(const Multiplier& other) {
+ fMagnitude = other.fMagnitude;
+ if (other.fArbitrary != nullptr) {
+ UErrorCode localStatus = U_ZERO_ERROR;
+ fArbitrary = new DecNum(*other.fArbitrary, localStatus);
+ } else {
+ fArbitrary = nullptr;
+ }
+ fError = other.fError;
+ return *this;
+}
+
+Multiplier::Multiplier(Multiplier&& src) U_NOEXCEPT
+ : fMagnitude(src.fMagnitude), fArbitrary(src.fArbitrary), fError(src.fError) {
+ // Take ownership away from src if necessary
+ src.fArbitrary = nullptr;
+}
+
+Multiplier& Multiplier::operator=(Multiplier&& src) U_NOEXCEPT {
+ fMagnitude = src.fMagnitude;
+ fArbitrary = src.fArbitrary;
+ fError = src.fError;
+ // Take ownership away from src if necessary
+ src.fArbitrary = nullptr;
+ return *this;
+}
+
+Multiplier::~Multiplier() {
+ delete fArbitrary;
+}
+
Multiplier Multiplier::none() {
- return {0, 1};
+ return {0, nullptr};
}
Multiplier Multiplier::powerOfTen(int32_t power) {
- return {power, 1};
+ return {power, nullptr};
}
Multiplier Multiplier::arbitraryDecimal(StringPiece multiplicand) {
- // TODO: Fix this hack
UErrorCode localError = U_ZERO_ERROR;
- DecimalQuantity dq;
- dq.setToDecNumber(multiplicand, localError);
- return {0, dq.toDouble()};
+ LocalPointer<DecNum> decnum(new DecNum(), localError);
+ if (U_FAILURE(localError)) {
+ return {localError};
+ }
+ decnum->setTo(multiplicand, localError);
+ if (U_FAILURE(localError)) {
+ return {localError};
+ }
+ return {0, decnum.orphan()};
}
Multiplier Multiplier::arbitraryDouble(double multiplicand) {
- return {0, multiplicand};
+ UErrorCode localError = U_ZERO_ERROR;
+ LocalPointer<DecNum> decnum(new DecNum(), localError);
+ if (U_FAILURE(localError)) {
+ return {localError};
+ }
+ decnum->setTo(multiplicand, localError);
+ if (U_FAILURE(localError)) {
+ return {localError};
+ }
+ return {0, decnum.orphan()};
}
void Multiplier::applyTo(impl::DecimalQuantity& quantity) const {
quantity.adjustMagnitude(fMagnitude);
- quantity.multiplyBy(fArbitrary);
+ if (fArbitrary != nullptr) {
+ UErrorCode localStatus = U_ZERO_ERROR;
+ quantity.multiplyBy(*fArbitrary, localStatus);
+ }
}
void Multiplier::applyReciprocalTo(impl::DecimalQuantity& quantity) const {
quantity.adjustMagnitude(-fMagnitude);
- if (fArbitrary != 0) {
- quantity.multiplyBy(1 / fArbitrary);
+ if (fArbitrary != nullptr) {
+ UErrorCode localStatus = U_ZERO_ERROR;
+ quantity.divideBy(*fArbitrary, localStatus);
}
}
// NOTE: MultiplierParseHandler is declared in the header numparse_validators.h
MultiplierParseHandler::MultiplierParseHandler(::icu::number::Multiplier multiplier)
- : fMultiplier(multiplier) {}
+ : fMultiplier(std::move(multiplier)) {}
void MultiplierParseHandler::postProcess(ParsedNumber& result) const {
if (!result.quantity.bogus) {
#include "number_decimalquantity.h"
#include "double-conversion.h"
#include "number_roundingutils.h"
+#include "putilimp.h"
using namespace icu;
using namespace icu::number;
CharString buffer;
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
- // Utilize DecimalQuantity/decNumber to parse this for us.
- // TODO: Parse to a DecNumber directly.
- DecimalQuantity dq;
- UErrorCode localStatus = U_ZERO_ERROR;
- dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
- if (U_FAILURE(localStatus)) {
- // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
+ LocalPointer<DecNum> decnum(new DecNum(), status);
+ if (U_FAILURE(status)) { return; }
+ decnum->setTo({buffer.data(), buffer.length()}, status);
+ if (U_FAILURE(status)) {
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
return;
}
- macros.multiplier = Multiplier::arbitraryDouble(dq.toDouble());
+
+ // NOTE: The constructor will optimize the decnum for us if possible.
+ macros.multiplier = {0, decnum.orphan()};
}
-void blueprint_helpers::generateMultiplierOption(int32_t magnitude, double arbitrary, UnicodeString& sb,
- UErrorCode&) {
+void
+blueprint_helpers::generateMultiplierOption(int32_t magnitude, const DecNum* arbitrary, UnicodeString& sb,
+ UErrorCode& status) {
// Utilize DecimalQuantity/double_conversion to format this for us.
DecimalQuantity dq;
- dq.setToDouble(arbitrary);
+ if (arbitrary != nullptr) {
+ dq.setToDecNum(*arbitrary, status);
+ } else {
+ dq.setToInt(1);
+ }
dq.adjustMagnitude(magnitude);
dq.roundToInfinity();
sb.append(dq.toPlainString());
// namespace for enums and entrypoint functions
namespace skeleton {
+///////////////////////////////////////////////////////////////////////////////////////
+// NOTE: For an example of how to add a new stem to the number skeleton parser, see: //
+// http://bugs.icu-project.org/trac/changeset/41193 //
+///////////////////////////////////////////////////////////////////////////////////////
+
/**
* While parsing a skeleton, this enum records what type of option we expect to find next.
*/
void parseMultiplierOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
-void generateMultiplierOption(int32_t magnitude, double arbitrary, UnicodeString& sb, UErrorCode& status);
+void generateMultiplierOption(int32_t magnitude, const DecNum* arbitrary, UnicodeString& sb,
+ UErrorCode& status);
} // namespace blueprint_helpers
--- /dev/null
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include <stdlib.h>
+#include <cmath>
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
+
+// Allow implicit conversion from char16_t* to UnicodeString for this file:
+// Helpful in toString methods and elsewhere.
+#define UNISTR_FROM_STRING_EXPLICIT
+
+#include "number_types.h"
+#include "number_utils.h"
+#include "charstr.h"
+#include "decContext.h"
+#include "decNumber.h"
+#include "double-conversion.h"
+
+using namespace icu;
+using namespace icu::number;
+using namespace icu::number::impl;
+
+using icu::double_conversion::DoubleToStringConverter;
+
+
+DecNum::DecNum() {
+ uprv_decContextDefault(&fContext, DEC_INIT_BASE);
+ uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN);
+ fContext.traps = 0; // no traps, thank you (what does this even mean?)
+}
+
+DecNum::DecNum(const DecNum& other, UErrorCode& status)
+ : fContext(other.fContext) {
+ // Allocate memory for the new DecNum.
+ U_ASSERT(fContext.digits == other.fData.getCapacity());
+ if (fContext.digits > kDefaultDigits) {
+ void* p = fData.resize(fContext.digits, 0);
+ if (p == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ }
+
+ // Copy the data from the old DecNum to the new one.
+ uprv_memcpy(fData.getAlias(), other.fData.getAlias(), sizeof(decNumber));
+ uprv_memcpy(fData.getArrayStart(),
+ other.fData.getArrayStart(),
+ other.fData.getArrayLimit() - other.fData.getArrayStart());
+}
+
+void DecNum::setTo(StringPiece str, UErrorCode& status) {
+ // We need NUL-terminated for decNumber; CharString guarantees this, but not StringPiece.
+ CharString cstr(str, status);
+ if (U_FAILURE(status)) { return; }
+ _setTo(cstr.data(), str.length(), status);
+}
+
+void DecNum::setTo(const char* str, UErrorCode& status) {
+ _setTo(str, static_cast<int32_t>(uprv_strlen(str)), status);
+}
+
+void DecNum::setTo(double d, UErrorCode& status) {
+ // Need to check for NaN and Infinity before going into DoubleToStringConverter
+ if (std::isnan(d) != 0 || std::isfinite(d) == 0) {
+ status = U_UNSUPPORTED_ERROR;
+ return;
+ }
+
+ // First convert from double to string, then string to DecNum.
+ // Allocate enough room for: all digits, "E-324", and NUL-terminator.
+ char buffer[DoubleToStringConverter::kBase10MaximalLength + 6];
+ bool sign; // unused; always positive
+ int32_t length;
+ int32_t point;
+ DoubleToStringConverter::DoubleToAscii(
+ d,
+ DoubleToStringConverter::DtoaMode::SHORTEST,
+ 0,
+ buffer,
+ sizeof(buffer),
+ &sign,
+ &length,
+ &point
+ );
+
+ // Read initial result as a string.
+ _setTo(buffer, length, status);
+
+ // Set exponent and bitmask. Note that DoubleToStringConverter does not do negatives.
+ fData.getAlias()->exponent += point - length;
+ fData.getAlias()->bits |= static_cast<uint8_t>(std::signbit(d) ? DECNEG : 0);
+}
+
+void DecNum::_setTo(const char* str, int32_t maxDigits, UErrorCode& status) {
+ if (maxDigits > kDefaultDigits) {
+ fData.resize(maxDigits, 0);
+ fContext.digits = maxDigits;
+ } else {
+ fContext.digits = kDefaultDigits;
+ }
+
+ static_assert(DECDPUN == 1, "Assumes that DECDPUN is set to 1");
+ uprv_decNumberFromString(fData.getAlias(), str, &fContext);
+
+ // For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity!
+ if (decNumberIsSpecial(fData.getAlias())) {
+ status = U_UNSUPPORTED_ERROR;
+ return;
+ }
+
+ // Check for invalid syntax and set the corresponding error code.
+ if ((fContext.status & DEC_Conversion_syntax) != 0) {
+ status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
+ } else if (fContext.status != 0) {
+ // Not a syntax error, but some other error, like an exponent that is too large.
+ status = U_UNSUPPORTED_ERROR;
+ }
+}
+
+void
+DecNum::setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status) {
+ if (length > kDefaultDigits) {
+ fData.resize(length, 0);
+ fContext.digits = length;
+ } else {
+ fContext.digits = kDefaultDigits;
+ }
+
+ // "digits is of type int32_t, and must have a value in the range 1 through 999,999,999."
+ if (length < 1 || length > 999999999) {
+ // Too large for decNumber
+ status = U_UNSUPPORTED_ERROR;
+ return;
+ }
+ // "The exponent field holds the exponent of the number. Its range is limited by the requirement that
+ // "the range of the adjusted exponent of the number be balanced and fit within a whole number of
+ // "decimal digits (in this implementation, be –999,999,999 through +999,999,999). The adjusted
+ // "exponent is the exponent that would result if the number were expressed with a single digit before
+ // "the decimal point, and is therefore given by exponent+digits-1."
+ if (scale > 999999999 - length + 1 || scale < -999999999 - length + 1) {
+ // Too large for decNumber
+ status = U_UNSUPPORTED_ERROR;
+ return;
+ }
+
+ fData.getAlias()->digits = length;
+ fData.getAlias()->exponent = scale;
+ fData.getAlias()->bits = static_cast<uint8_t>(isNegative ? DECNEG : 0);
+ uprv_decNumberSetBCD(fData, bcd, static_cast<uint32_t>(length));
+ if (fContext.status != 0) {
+ // Some error occured while constructing the decNumber.
+ status = U_INTERNAL_PROGRAM_ERROR;
+ }
+}
+
+void DecNum::normalize() {
+ uprv_decNumberReduce(fData, fData, &fContext);
+}
+
+void DecNum::multiplyBy(const DecNum& rhs, UErrorCode& status) {
+ uprv_decNumberMultiply(fData, fData, rhs.fData, &fContext);
+ if (fContext.status != 0) {
+ status = U_INTERNAL_PROGRAM_ERROR;
+ }
+}
+
+void DecNum::divideBy(const DecNum& rhs, UErrorCode& status) {
+ uprv_decNumberDivide(fData, fData, rhs.fData, &fContext);
+ if (fContext.status != 0) {
+ status = U_INTERNAL_PROGRAM_ERROR;
+ }
+}
+
+bool DecNum::isNegative() const {
+ return decNumberIsNegative(fData.getAlias());
+}
+
+bool DecNum::isZero() const {
+ return decNumberIsZero(fData.getAlias());
+}
+
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
#include "number_patternstring.h"
#include "number_modifiers.h"
#include "number_multiplier.h"
+#include "decNumber.h"
+#include "charstr.h"
U_NAMESPACE_BEGIN namespace number {
namespace impl {
return uprv_strcmp("permille", unit.getSubtype()) == 0;
}
+
+/** A very thin C++ wrapper around decNumber.h */
+class DecNum : public UMemory {
+ public:
+ DecNum(); // leaves object in valid but undefined state
+
+ // Copy-like constructor; use the default move operators.
+ DecNum(const DecNum& other, UErrorCode& status);
+
+ /** Sets the decNumber to the StringPiece. */
+ void setTo(StringPiece str, UErrorCode& status);
+
+ /** Sets the decNumber to the NUL-terminated char string. */
+ void setTo(const char* str, UErrorCode& status);
+
+ /** Uses double_conversion to set this decNumber to the given double. */
+ void setTo(double d, UErrorCode& status);
+
+ /** Sets the decNumber to the BCD representation. */
+ void setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status);
+
+ void normalize();
+
+ void multiplyBy(const DecNum& rhs, UErrorCode& status);
+
+ void divideBy(const DecNum& rhs, UErrorCode& status);
+
+ bool isNegative() const;
+
+ bool isZero() const;
+
+ inline const decNumber* getRawDecNumber() const {
+ return fData.getAlias();
+ }
+
+ private:
+ static constexpr int32_t kDefaultDigits = 34;
+ MaybeStackHeaderAndArray<decNumber, char, kDefaultDigits> fData;
+ decContext fContext;
+
+ void _setTo(const char* str, int32_t maxDigits, UErrorCode& status);
+};
+
+
} // namespace impl
} // namespace number
U_NAMESPACE_END
#include "numparse_unisets.h"
#include "numparse_utils.h"
#include "unicode/uchar.h"
+#include "putilimp.h"
using namespace icu;
using namespace icu::numparse;
class MultiplierFormatHandler;
class CurrencySymbols;
class GeneratorHelpers;
+class DecNum;
} // namespace impl
*/
static Multiplier arbitraryDouble(double multiplicand);
+ // We need a custom destructor for the DecNum, which means we need to declare
+ // the copy/move constructor/assignment quartet.
+
+ /** @draft ICU 62 */
+ Multiplier(const Multiplier& other);
+
+ /** @draft ICU 62 */
+ Multiplier& operator=(const Multiplier& other);
+
+ /** @draft ICU 62 */
+ Multiplier(Multiplier&& src) U_NOEXCEPT;
+
+ /** @draft ICU 62 */
+ Multiplier& operator=(Multiplier&& src) U_NOEXCEPT;
+
+ /** @draft ICU 62 */
+ ~Multiplier();
+
+ /** @internal */
+ Multiplier(int32_t magnitude, impl::DecNum* arbitraryToAdopt);
+
private:
int32_t fMagnitude;
- double fArbitrary;
+ impl::DecNum* fArbitrary;
+ UErrorCode fError;
- Multiplier(int32_t magnitude, double arbitrary);
+ Multiplier(UErrorCode error) : fMagnitude(0), fArbitrary(nullptr), fError(error) {}
- Multiplier() : fMagnitude(0), fArbitrary(1) {}
+ Multiplier() : fMagnitude(0), fArbitrary(nullptr), fError(U_ZERO_ERROR) {}
bool isValid() const {
- return fMagnitude != 0 || fArbitrary != 1;
+ return fMagnitude != 0 || fArbitrary != nullptr;
+ }
+
+ UBool copyErrorTo(UErrorCode &status) const {
+ if (fError != U_ZERO_ERROR) {
+ status = fError;
+ return TRUE;
+ }
+ return FALSE;
}
void applyTo(impl::DecimalQuantity& quantity) const;
SymbolsWrapper(const SymbolsWrapper &other);
/** @internal */
- SymbolsWrapper(SymbolsWrapper&& src) U_NOEXCEPT;
+ SymbolsWrapper &operator=(const SymbolsWrapper &other);
/** @internal */
- ~SymbolsWrapper();
+ SymbolsWrapper(SymbolsWrapper&& src) U_NOEXCEPT;
/** @internal */
- SymbolsWrapper &operator=(const SymbolsWrapper &other);
+ SymbolsWrapper &operator=(SymbolsWrapper&& src) U_NOEXCEPT;
/** @internal */
- SymbolsWrapper &operator=(SymbolsWrapper&& src) U_NOEXCEPT;
+ ~SymbolsWrapper();
#ifndef U_HIDE_INTERNAL_API
bool copyErrorTo(UErrorCode &status) const {
return notation.copyErrorTo(status) || rounder.copyErrorTo(status) ||
padder.copyErrorTo(status) || integerWidth.copyErrorTo(status) ||
- symbols.copyErrorTo(status);
+ symbols.copyErrorTo(status) || multiplier.copyErrorTo(status);
}
};
#include "charstr.h"
#include <cstdarg>
+#include <cmath>
#include "unicode/unum.h"
#include "unicode/numberformatter.h"
#include "number_types.h"
Locale::getEnglish(),
2,
u"-10.4");
+
+ assertFormatSingle(
+ u"Negative One Multiplier",
+ u"multiply/-1",
+ NumberFormatter::with().multiplier(Multiplier::arbitraryDouble(-1)),
+ Locale::getEnglish(),
+ 444444,
+ u"-444,444");
}
void NumberFormatterApiTest::locale() {
for (int32_t i = 0; i < 9; i++) {
double d = inputs[i];
UnicodeString actual3 = l3.formatDouble(d, status).toString();
- assertEquals(message + ": Skeleton Path: " + d, expecteds[i], actual3);
+ assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
}
} else {
assertUndefinedSkeleton(f);
for (int32_t i = 0; i < 9; i++) {
double d = inputs[i];
UnicodeString actual3 = l3.formatDouble(d, status).toString();
- assertEquals(message + ": Skeleton Path: " + d, expecteds[i], actual3);
+ assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
}
} else {
assertUndefinedSkeleton(f);
assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
LocalizedNumberFormatter l3 = NumberFormatter::fromSkeleton(normalized, status).locale(locale);
UnicodeString actual3 = l3.formatDouble(input, status).toString();
- assertEquals(message + ": Skeleton Path: " + input, expected, actual3);
+ assertEquals(message + ": Skeleton Path: '" + normalized + "': " + input, expected, actual3);
} else {
assertUndefinedSkeleton(f);
}
u"round-currency-cash/XXX",
u"scientific/ee",
u"round-increment/xxx",
+ u"round-increment/NaN",
u"round-increment/0.1.2",
u"multiply/xxx",
+ u"multiply/NaN",
u"multiply/0.1.2",
u"multiply/français", // non-invariant characters for C++
u"currency/dummy",
*/
class NumberSkeletonImpl {
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // NOTE: For an example of how to add a new stem to the number skeleton parser, see: //
+ // http://bugs.icu-project.org/trac/changeset/41193 //
+ ///////////////////////////////////////////////////////////////////////////////////////
+
/**
* While parsing a skeleton, this enum records what type of option we expect to find next.
*/
ULocale.ENGLISH,
2,
"-10.4");
+
+ assertFormatSingle(
+ "Negative One Multiplier",
+ "multiply/-1",
+ NumberFormatter.with().multiplier(Multiplier.arbitrary(-1)),
+ ULocale.ENGLISH,
+ 444444,
+ "-444,444");
}
@Test
"round-currency-cash/XXX",
"scientific/ee",
"round-increment/xxx",
+ "round-increment/NaN",
"round-increment/0.1.2",
"multiply/xxx",
+ "multiply/NaN",
"multiply/0.1.2",
"multiply/français", // non-invariant characters for C++
"currency/dummy",