| Index: icu46/source/i18n/digitlst.cpp
|
| ===================================================================
|
| --- icu46/source/i18n/digitlst.cpp (revision 0)
|
| +++ icu46/source/i18n/digitlst.cpp (revision 0)
|
| @@ -0,0 +1,861 @@
|
| +/*
|
| +**********************************************************************
|
| +* Copyright (C) 1997-2010, International Business Machines
|
| +* Corporation and others. All Rights Reserved.
|
| +**********************************************************************
|
| +*
|
| +* File DIGITLST.CPP
|
| +*
|
| +* Modification History:
|
| +*
|
| +* Date Name Description
|
| +* 03/21/97 clhuang Converted from java.
|
| +* 03/21/97 clhuang Implemented with new APIs.
|
| +* 03/27/97 helena Updated to pass the simple test after code review.
|
| +* 03/31/97 aliu Moved isLONG_MIN to here, and fixed it.
|
| +* 04/15/97 aliu Changed MAX_COUNT to DBL_DIG. Changed Digit to char.
|
| +* Reworked representation by replacing fDecimalAt
|
| +* with fExponent.
|
| +* 04/16/97 aliu Rewrote set() and getDouble() to use sprintf/atof
|
| +* to do digit conversion.
|
| +* 09/09/97 aliu Modified for exponential notation support.
|
| +* 08/02/98 stephen Added nearest/even rounding
|
| +* Fixed bug in fitsIntoLong
|
| +******************************************************************************
|
| +*/
|
| +
|
| +#include "digitlst.h"
|
| +
|
| +#if !UCONFIG_NO_FORMATTING
|
| +#include "unicode/putil.h"
|
| +#include "charstr.h"
|
| +#include "cmemory.h"
|
| +#include "cstring.h"
|
| +#include "putilimp.h"
|
| +#include "uassert.h"
|
| +#include <stdlib.h>
|
| +#include <limits.h>
|
| +#include <string.h>
|
| +#include <stdio.h>
|
| +#include <limits>
|
| +
|
| +// ***************************************************************************
|
| +// class DigitList
|
| +// A wrapper onto decNumber.
|
| +// Used to be standalone.
|
| +// ***************************************************************************
|
| +
|
| +/**
|
| + * This is the zero digit. The base for the digits returned by getDigit()
|
| + * Note that it is the platform invariant digit, and is not Unicode.
|
| + */
|
| +#define kZero '0'
|
| +
|
| +static char gDecimal = 0;
|
| +
|
| +/* Only for 32 bit numbers. Ignore the negative sign. */
|
| +static const char LONG_MIN_REP[] = "2147483648";
|
| +static const char I64_MIN_REP[] = "9223372036854775808";
|
| +
|
| +
|
| +U_NAMESPACE_BEGIN
|
| +
|
| +// -------------------------------------
|
| +// default constructor
|
| +
|
| +DigitList::DigitList()
|
| +{
|
| + uprv_decContextDefault(&fContext, DEC_INIT_BASE);
|
| + fContext.traps = 0;
|
| + uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN);
|
| + fContext.digits = fStorage.getCapacity();
|
| +
|
| + fDecNumber = fStorage.getAlias();
|
| + uprv_decNumberZero(fDecNumber);
|
| +
|
| + fDouble = 0.0;
|
| + fHaveDouble = TRUE;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +
|
| +DigitList::~DigitList()
|
| +{
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// copy constructor
|
| +
|
| +DigitList::DigitList(const DigitList &other)
|
| +{
|
| + fDecNumber = fStorage.getAlias();
|
| + *this = other;
|
| +}
|
| +
|
| +
|
| +// -------------------------------------
|
| +// assignment operator
|
| +
|
| +DigitList&
|
| +DigitList::operator=(const DigitList& other)
|
| +{
|
| + if (this != &other)
|
| + {
|
| + uprv_memcpy(&fContext, &other.fContext, sizeof(decContext));
|
| +
|
| + if (other.fStorage.getCapacity() > fStorage.getCapacity()) {
|
| + fDecNumber = fStorage.resize(other.fStorage.getCapacity());
|
| + }
|
| + // Always reset the fContext.digits, even if fDecNumber was not reallocated,
|
| + // because above we copied fContext from other.fContext.
|
| + fContext.digits = fStorage.getCapacity();
|
| + uprv_decNumberCopy(fDecNumber, other.fDecNumber);
|
| +
|
| + fDouble = other.fDouble;
|
| + fHaveDouble = other.fHaveDouble;
|
| + }
|
| + return *this;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// operator == (does not exactly match the old DigitList function)
|
| +
|
| +UBool
|
| +DigitList::operator==(const DigitList& that) const
|
| +{
|
| + if (this == &that) {
|
| + return TRUE;
|
| + }
|
| + decNumber n; // Has space for only a none digit value.
|
| + decContext c;
|
| + uprv_decContextDefault(&c, DEC_INIT_BASE);
|
| + c.digits = 1;
|
| + c.traps = 0;
|
| +
|
| + uprv_decNumberCompare(&n, this->fDecNumber, that.fDecNumber, &c);
|
| + UBool result = decNumberIsZero(&n);
|
| + return result;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// comparison function. Returns
|
| +// Not Comparable : -2
|
| +// < : -1
|
| +// == : 0
|
| +// > : +1
|
| +int32_t DigitList::compare(const DigitList &other) {
|
| + decNumber result;
|
| + int32_t savedDigits = fContext.digits;
|
| + fContext.digits = 1;
|
| + uprv_decNumberCompare(&result, this->fDecNumber, other.fDecNumber, &fContext);
|
| + fContext.digits = savedDigits;
|
| + if (decNumberIsZero(&result)) {
|
| + return 0;
|
| + } else if (decNumberIsSpecial(&result)) {
|
| + return -2;
|
| + } else if (result.bits & DECNEG) {
|
| + return -1;
|
| + } else {
|
| + return 1;
|
| + }
|
| +}
|
| +
|
| +
|
| +// -------------------------------------
|
| +// Reduce - remove trailing zero digits.
|
| +void
|
| +DigitList::reduce() {
|
| + uprv_decNumberReduce(fDecNumber, fDecNumber, &fContext);
|
| +}
|
| +
|
| +
|
| +// -------------------------------------
|
| +// trim - remove trailing fraction zero digits.
|
| +void
|
| +DigitList::trim() {
|
| + uprv_decNumberTrim(fDecNumber);
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// Resets the digit list; sets all the digits to zero.
|
| +
|
| +void
|
| +DigitList::clear()
|
| +{
|
| + uprv_decNumberZero(fDecNumber);
|
| + uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN);
|
| + fDouble = 0.0;
|
| + fHaveDouble = TRUE;
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Formats a int64_t number into a base 10 string representation, and NULL terminates it.
|
| + * @param number The number to format
|
| + * @param outputStr The string to output to. Must be at least MAX_DIGITS+2 in length (21),
|
| + * to hold the longest int64_t value.
|
| + * @return the number of digits written, not including the sign.
|
| + */
|
| +static int32_t
|
| +formatBase10(int64_t number, char *outputStr) {
|
| + // The number is output backwards, starting with the LSD.
|
| + // Fill the buffer from the far end. After the number is complete,
|
| + // slide the string contents to the front.
|
| +
|
| + const int32_t MAX_IDX = MAX_DIGITS+2;
|
| + int32_t destIdx = MAX_IDX;
|
| + outputStr[--destIdx] = 0;
|
| +
|
| + int64_t n = number;
|
| + if (number < 0) { // Negative numbers are slightly larger than a postive
|
| + outputStr[--destIdx] = (char)(-(n % 10) + kZero);
|
| + n /= -10;
|
| + }
|
| + do {
|
| + outputStr[--destIdx] = (char)(n % 10 + kZero);
|
| + n /= 10;
|
| + } while (n > 0);
|
| +
|
| + if (number < 0) {
|
| + outputStr[--destIdx] = '-';
|
| + }
|
| +
|
| + // Slide the number to the start of the output str
|
| + U_ASSERT(destIdx >= 0);
|
| + int32_t length = MAX_IDX - destIdx;
|
| + uprv_memmove(outputStr, outputStr+MAX_IDX-length, length);
|
| +
|
| + return length;
|
| +}
|
| +
|
| +
|
| +// -------------------------------------
|
| +
|
| +void
|
| +DigitList::setRoundingMode(DecimalFormat::ERoundingMode m) {
|
| + enum rounding r;
|
| +
|
| + switch (m) {
|
| + case DecimalFormat::kRoundCeiling: r = DEC_ROUND_CEILING; break;
|
| + case DecimalFormat::kRoundFloor: r = DEC_ROUND_FLOOR; break;
|
| + case DecimalFormat::kRoundDown: r = DEC_ROUND_DOWN; break;
|
| + case DecimalFormat::kRoundUp: r = DEC_ROUND_UP; break;
|
| + case DecimalFormat::kRoundHalfEven: r = DEC_ROUND_HALF_EVEN; break;
|
| + case DecimalFormat::kRoundHalfDown: r = DEC_ROUND_HALF_DOWN; break;
|
| + case DecimalFormat::kRoundHalfUp: r = DEC_ROUND_HALF_UP; break;
|
| + default:
|
| + // TODO: how to report the problem?
|
| + // Leave existing mode unchanged.
|
| + r = uprv_decContextGetRounding(&fContext);
|
| + }
|
| + uprv_decContextSetRounding(&fContext, r);
|
| +
|
| +}
|
| +
|
| +
|
| +// -------------------------------------
|
| +
|
| +void
|
| +DigitList::setPositive(UBool s) {
|
| + if (s) {
|
| + fDecNumber->bits &= ~DECNEG;
|
| + } else {
|
| + fDecNumber->bits |= DECNEG;
|
| + }
|
| + fHaveDouble = FALSE;
|
| +}
|
| +// -------------------------------------
|
| +
|
| +void
|
| +DigitList::setDecimalAt(int32_t d) {
|
| + U_ASSERT((fDecNumber->bits & DECSPECIAL) == 0); // Not Infinity or NaN
|
| + U_ASSERT(d-1>-999999999);
|
| + U_ASSERT(d-1< 999999999);
|
| + int32_t adjustedDigits = fDecNumber->digits;
|
| + if (decNumberIsZero(fDecNumber)) {
|
| + // Account for difference in how zero is represented between DigitList & decNumber.
|
| + adjustedDigits = 0;
|
| + }
|
| + fDecNumber->exponent = d - adjustedDigits;
|
| + fHaveDouble = FALSE;
|
| +}
|
| +
|
| +int32_t
|
| +DigitList::getDecimalAt() {
|
| + U_ASSERT((fDecNumber->bits & DECSPECIAL) == 0); // Not Infinity or NaN
|
| + if (decNumberIsZero(fDecNumber) || ((fDecNumber->bits & DECSPECIAL) != 0)) {
|
| + return fDecNumber->exponent; // Exponent should be zero for these cases.
|
| + }
|
| + return fDecNumber->exponent + fDecNumber->digits;
|
| +}
|
| +
|
| +void
|
| +DigitList::setCount(int32_t c) {
|
| + U_ASSERT(c <= fContext.digits);
|
| + if (c == 0) {
|
| + // For a value of zero, DigitList sets all fields to zero, while
|
| + // decNumber keeps one digit (with that digit being a zero)
|
| + c = 1;
|
| + fDecNumber->lsu[0] = 0;
|
| + }
|
| + fDecNumber->digits = c;
|
| + fHaveDouble = FALSE;
|
| +}
|
| +
|
| +int32_t
|
| +DigitList::getCount() const {
|
| + if (decNumberIsZero(fDecNumber) && fDecNumber->exponent==0) {
|
| + // The extra test for exponent==0 is needed because parsing sometimes appends
|
| + // zero digits. It's bogus, decimalFormatter parsing needs to be cleaned up.
|
| + return 0;
|
| + } else {
|
| + return fDecNumber->digits;
|
| + }
|
| +}
|
| +
|
| +void
|
| +DigitList::setDigit(int32_t i, char v) {
|
| + int32_t count = fDecNumber->digits;
|
| + U_ASSERT(i<count);
|
| + U_ASSERT(v>='0' && v<='9');
|
| + v &= 0x0f;
|
| + fDecNumber->lsu[count-i-1] = v;
|
| + fHaveDouble = FALSE;
|
| +}
|
| +
|
| +char
|
| +DigitList::getDigit(int32_t i) {
|
| + int32_t count = fDecNumber->digits;
|
| + U_ASSERT(i<count);
|
| + return fDecNumber->lsu[count-i-1] + '0';
|
| +}
|
| +
|
| +// copied from DigitList::getDigit()
|
| +uint8_t
|
| +DigitList::getDigitValue(int32_t i) {
|
| + int32_t count = fDecNumber->digits;
|
| + U_ASSERT(i<count);
|
| + return fDecNumber->lsu[count-i-1];
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// Appends the digit to the digit list if it's not out of scope.
|
| +// Ignores the digit, otherwise.
|
| +//
|
| +// This function is horribly inefficient to implement with decNumber because
|
| +// the digits are stored least significant first, which requires moving all
|
| +// existing digits down one to make space for the new one to be appended.
|
| +//
|
| +void
|
| +DigitList::append(char digit)
|
| +{
|
| + U_ASSERT(digit>='0' && digit<='9');
|
| + // Ignore digits which exceed the precision we can represent
|
| + // And don't fix for larger precision. Fix callers instead.
|
| + if (decNumberIsZero(fDecNumber)) {
|
| + // Zero needs to be special cased because of the difference in the way
|
| + // that the old DigitList and decNumber represent it.
|
| + // digit cout was zero for digitList, is one for decNumber
|
| + fDecNumber->lsu[0] = digit & 0x0f;
|
| + fDecNumber->digits = 1;
|
| + fDecNumber->exponent--; // To match the old digit list implementation.
|
| + } else {
|
| + int32_t nDigits = fDecNumber->digits;
|
| + if (nDigits < fContext.digits) {
|
| + int i;
|
| + for (i=nDigits; i>0; i--) {
|
| + fDecNumber->lsu[i] = fDecNumber->lsu[i-1];
|
| + }
|
| + fDecNumber->lsu[0] = digit & 0x0f;
|
| + fDecNumber->digits++;
|
| + // DigitList emulation - appending doesn't change the magnitude of existing
|
| + // digits. With decNumber's decimal being after the
|
| + // least signficant digit, we need to adjust the exponent.
|
| + fDecNumber->exponent--;
|
| + }
|
| + }
|
| + fHaveDouble = FALSE;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +
|
| +/**
|
| + * Currently, getDouble() depends on atof() to do its conversion.
|
| + *
|
| + * WARNING!!
|
| + * This is an extremely costly function. ~1/2 of the conversion time
|
| + * can be linked to this function.
|
| + */
|
| +double
|
| +DigitList::getDouble() const
|
| +{
|
| + // TODO: fix thread safety. Can probably be finessed some by analyzing
|
| + // what public const functions can see which DigitLists.
|
| + // Like precompute fDouble for DigitLists coming in from a parse
|
| + // or from a Formattable::set(), but not for any others.
|
| + if (fHaveDouble) {
|
| + return fDouble;
|
| + }
|
| + DigitList *nonConstThis = const_cast<DigitList *>(this);
|
| +
|
| + if (gDecimal == 0) {
|
| + char rep[MAX_DIGITS];
|
| + // For machines that decide to change the decimal on you,
|
| + // and try to be too smart with localization.
|
| + // This normally should be just a '.'.
|
| + sprintf(rep, "%+1.1f", 1.0);
|
| + gDecimal = rep[2];
|
| + }
|
| +
|
| + if (isZero()) {
|
| + nonConstThis->fDouble = 0.0;
|
| + if (decNumberIsNegative(fDecNumber)) {
|
| + nonConstThis->fDouble /= -1;
|
| + }
|
| + } else if (isInfinite()) {
|
| + if (std::numeric_limits<double>::has_infinity) {
|
| + nonConstThis->fDouble = std::numeric_limits<double>::infinity();
|
| + } else {
|
| + nonConstThis->fDouble = std::numeric_limits<double>::max();
|
| + }
|
| + if (!isPositive()) {
|
| + nonConstThis->fDouble = -fDouble;
|
| + }
|
| + } else {
|
| + MaybeStackArray<char, MAX_DBL_DIGITS+18> s;
|
| + // Note: 14 is a magic constant from the decNumber library documentation,
|
| + // the max number of extra characters beyond the number of digits
|
| + // needed to represent the number in string form. Add a few more
|
| + // for the additional digits we retain.
|
| +
|
| + // Round down to appx. double precision, if the number is longer than that.
|
| + // Copy the number first, so that we don't modify the original.
|
| + if (getCount() > MAX_DBL_DIGITS + 3) {
|
| + DigitList numToConvert(*this);
|
| + numToConvert.reduce(); // Removes any trailing zeros, so that digit count is good.
|
| + numToConvert.round(MAX_DBL_DIGITS+3);
|
| + uprv_decNumberToString(numToConvert.fDecNumber, s);
|
| + // TODO: how many extra digits should be included for an accurate conversion?
|
| + } else {
|
| + uprv_decNumberToString(this->fDecNumber, s);
|
| + }
|
| + U_ASSERT(uprv_strlen(&s[0]) < MAX_DBL_DIGITS+18);
|
| +
|
| + if (gDecimal != '.') {
|
| + char *decimalPt = strchr(s, '.');
|
| + if (decimalPt != NULL) {
|
| + *decimalPt = gDecimal;
|
| + }
|
| + }
|
| + char *end = NULL;
|
| + nonConstThis->fDouble = uprv_strtod(s, &end);
|
| + }
|
| + nonConstThis->fHaveDouble = TRUE;
|
| + return fDouble;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +
|
| +/**
|
| + * convert this number to an int32_t. Round if there is a fractional part.
|
| + * Return zero if the number cannot be represented.
|
| + */
|
| +int32_t DigitList::getLong() /*const*/
|
| +{
|
| + int32_t result = 0;
|
| + if (fDecNumber->digits + fDecNumber->exponent > 10) {
|
| + // Overflow, absolute value too big.
|
| + return result;
|
| + }
|
| + if (fDecNumber->exponent != 0) {
|
| + // Force to an integer, with zero exponent, rounding if necessary.
|
| + // (decNumberToInt32 will only work if the exponent is exactly zero.)
|
| + DigitList copy(*this);
|
| + DigitList zero;
|
| + uprv_decNumberQuantize(copy.fDecNumber, copy.fDecNumber, zero.fDecNumber, &fContext);
|
| + result = uprv_decNumberToInt32(copy.fDecNumber, &fContext);
|
| + } else {
|
| + result = uprv_decNumberToInt32(fDecNumber, &fContext);
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +
|
| +/**
|
| + * convert this number to an int64_t. Round if there is a fractional part.
|
| + * Return zero if the number cannot be represented.
|
| + */
|
| +int64_t DigitList::getInt64() /*const*/ {
|
| + // Round if non-integer. (Truncate or round?)
|
| + // Return 0 if out of range.
|
| + // Range of in64_t is -9223372036854775808 to 9223372036854775807 (19 digits)
|
| + //
|
| + if (fDecNumber->digits + fDecNumber->exponent > 19) {
|
| + // Overflow, absolute value too big.
|
| + return 0;
|
| + }
|
| + decNumber *workingNum = fDecNumber;
|
| +
|
| + if (fDecNumber->exponent != 0) {
|
| + // Force to an integer, with zero exponent, rounding if necessary.
|
| + DigitList copy(*this);
|
| + DigitList zero;
|
| + uprv_decNumberQuantize(copy.fDecNumber, copy.fDecNumber, zero.fDecNumber, &fContext);
|
| + workingNum = copy.fDecNumber;
|
| + }
|
| +
|
| + uint64_t value = 0;
|
| + int32_t numDigits = workingNum->digits;
|
| + for (int i = numDigits-1; i>=0 ; --i) {
|
| + int v = workingNum->lsu[i];
|
| + value = value * (uint64_t)10 + (uint64_t)v;
|
| + }
|
| + if (decNumberIsNegative(workingNum)) {
|
| + value = ~value;
|
| + value += 1;
|
| + }
|
| + int64_t svalue = (int64_t)value;
|
| +
|
| + // Check overflow. It's convenient that the MSD is 9 only on overflow, the amount of
|
| + // overflow can't wrap too far. The test will also fail -0, but
|
| + // that does no harm; the right answer is 0.
|
| + if (numDigits == 19) {
|
| + if (( decNumberIsNegative(fDecNumber) && svalue>0) ||
|
| + (!decNumberIsNegative(fDecNumber) && svalue<0)) {
|
| + svalue = 0;
|
| + }
|
| + }
|
| +
|
| + return svalue;
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Return a string form of this number.
|
| + * Format is as defined by the decNumber library, for interchange of
|
| + * decimal numbers.
|
| + */
|
| +void DigitList::getDecimal(CharString &str, UErrorCode &status) {
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| +
|
| + // A decimal number in string form can, worst case, be 14 characters longer
|
| + // than the number of digits. So says the decNumber library doc.
|
| + int32_t maxLength = fDecNumber->digits + 14;
|
| + int32_t capacity = 0;
|
| + char *buffer = str.clear().getAppendBuffer(maxLength, 0, capacity, status);
|
| + if (U_FAILURE(status)) {
|
| + return; // Memory allocation error on growing the string.
|
| + }
|
| + U_ASSERT(capacity >= maxLength);
|
| + uprv_decNumberToString(this->fDecNumber, buffer);
|
| + U_ASSERT((int32_t)uprv_strlen(buffer) <= maxLength);
|
| + str.append(buffer, -1, status);
|
| +}
|
| +
|
| +/**
|
| + * Return true if this is an integer value that can be held
|
| + * by an int32_t type.
|
| + */
|
| +UBool
|
| +DigitList::fitsIntoLong(UBool ignoreNegativeZero) /*const*/
|
| +{
|
| + if (decNumberIsSpecial(this->fDecNumber)) {
|
| + // NaN or Infinity. Does not fit in int32.
|
| + return FALSE;
|
| + }
|
| + uprv_decNumberTrim(this->fDecNumber);
|
| + if (fDecNumber->exponent < 0) {
|
| + // Number contains fraction digits.
|
| + return FALSE;
|
| + }
|
| + if (decNumberIsZero(this->fDecNumber) && !ignoreNegativeZero &&
|
| + (fDecNumber->bits & DECNEG) != 0) {
|
| + // Negative Zero, not ingored. Cannot represent as a long.
|
| + return FALSE;
|
| + }
|
| + if (fDecNumber->digits + fDecNumber->exponent < 10) {
|
| + // The number is 9 or fewer digits.
|
| + // The max and min int32 are 10 digts, so this number fits.
|
| + // This is the common case.
|
| + return TRUE;
|
| + }
|
| +
|
| + // TODO: Should cache these constants; construction is relatively costly.
|
| + // But not of huge consequence; they're only needed for 10 digit ints.
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + DigitList min32; min32.set("-2147483648", status);
|
| + if (this->compare(min32) < 0) {
|
| + return FALSE;
|
| + }
|
| + DigitList max32; max32.set("2147483647", status);
|
| + if (this->compare(max32) > 0) {
|
| + return FALSE;
|
| + }
|
| + if (U_FAILURE(status)) {
|
| + return FALSE;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +
|
| +/**
|
| + * Return true if the number represented by this object can fit into
|
| + * a long.
|
| + */
|
| +UBool
|
| +DigitList::fitsIntoInt64(UBool ignoreNegativeZero) /*const*/
|
| +{
|
| + if (decNumberIsSpecial(this->fDecNumber)) {
|
| + // NaN or Infinity. Does not fit in int32.
|
| + return FALSE;
|
| + }
|
| + uprv_decNumberTrim(this->fDecNumber);
|
| + if (fDecNumber->exponent < 0) {
|
| + // Number contains fraction digits.
|
| + return FALSE;
|
| + }
|
| + if (decNumberIsZero(this->fDecNumber) && !ignoreNegativeZero &&
|
| + (fDecNumber->bits & DECNEG) != 0) {
|
| + // Negative Zero, not ingored. Cannot represent as a long.
|
| + return FALSE;
|
| + }
|
| + if (fDecNumber->digits + fDecNumber->exponent < 19) {
|
| + // The number is 18 or fewer digits.
|
| + // The max and min int64 are 19 digts, so this number fits.
|
| + // This is the common case.
|
| + return TRUE;
|
| + }
|
| +
|
| + // TODO: Should cache these constants; construction is relatively costly.
|
| + // But not of huge consequence; they're only needed for 19 digit ints.
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + DigitList min64; min64.set("-9223372036854775808", status);
|
| + if (this->compare(min64) < 0) {
|
| + return FALSE;
|
| + }
|
| + DigitList max64; max64.set("9223372036854775807", status);
|
| + if (this->compare(max64) > 0) {
|
| + return FALSE;
|
| + }
|
| + if (U_FAILURE(status)) {
|
| + return FALSE;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +// -------------------------------------
|
| +
|
| +void
|
| +DigitList::set(int32_t source)
|
| +{
|
| + set((int64_t)source);
|
| + fDouble = source;
|
| + fHaveDouble = TRUE;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +/**
|
| + * @param maximumDigits The maximum digits to be generated. If zero,
|
| + * there is no maximum -- generate all digits.
|
| + */
|
| +void
|
| +DigitList::set(int64_t source)
|
| +{
|
| + char str[MAX_DIGITS+2]; // Leave room for sign and trailing nul.
|
| + formatBase10(source, str);
|
| + U_ASSERT(uprv_strlen(str) < sizeof(str));
|
| +
|
| + uprv_decNumberFromString(fDecNumber, str, &fContext);
|
| + fDouble = (double)source;
|
| + fHaveDouble = TRUE;
|
| +}
|
| +
|
| +
|
| +// -------------------------------------
|
| +/**
|
| + * Set the DigitList from a decimal number string.
|
| + *
|
| + * The incoming string _must_ be nul terminated, even though it is arriving
|
| + * as a StringPiece because that is what the decNumber library wants.
|
| + * We can get away with this for an internal function; it would not
|
| + * be acceptable for a public API.
|
| + */
|
| +void
|
| +DigitList::set(const StringPiece &source, UErrorCode &status) {
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| +
|
| + // Figure out a max number of digits to use during the conversion, and
|
| + // resize the number up if necessary.
|
| + int32_t numDigits = source.length();
|
| + if (numDigits > fContext.digits) {
|
| + // fContext.digits == fStorage.getCapacity()
|
| + decNumber *t = fStorage.resize(numDigits, fStorage.getCapacity());
|
| + if (t == NULL) {
|
| + status = U_MEMORY_ALLOCATION_ERROR;
|
| + return;
|
| + }
|
| + fDecNumber = t;
|
| + fContext.digits = numDigits;
|
| + }
|
| +
|
| + fContext.status = 0;
|
| + uprv_decNumberFromString(fDecNumber, source.data(), &fContext);
|
| + if ((fContext.status & DEC_Conversion_syntax) != 0) {
|
| + status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
|
| + }
|
| + fHaveDouble = FALSE;
|
| +}
|
| +
|
| +/**
|
| + * Set the digit list to a representation of the given double value.
|
| + * This method supports both fixed-point and exponential notation.
|
| + * @param source Value to be converted.
|
| + */
|
| +void
|
| +DigitList::set(double source)
|
| +{
|
| + // for now, simple implementation; later, do proper IEEE stuff
|
| + char rep[MAX_DIGITS + 8]; // Extra space for '+', '.', e+NNN, and '\0' (actually +8 is enough)
|
| +
|
| + // Generate a representation of the form /[+-][0-9]+e[+-][0-9]+/
|
| + sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source);
|
| + U_ASSERT(uprv_strlen(rep) < sizeof(rep));
|
| +
|
| + // Create a decNumber from the string.
|
| + uprv_decNumberFromString(fDecNumber, rep, &fContext);
|
| + uprv_decNumberTrim(fDecNumber);
|
| + fDouble = source;
|
| + fHaveDouble = TRUE;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +
|
| +/*
|
| + * Multiply
|
| + * The number will be expanded if need be to retain full precision.
|
| + * In practice, for formatting, multiply is by 10, 100 or 1000, so more digits
|
| + * will not be required for this use.
|
| + */
|
| +void
|
| +DigitList::mult(const DigitList &other, UErrorCode &status) {
|
| + fContext.status = 0;
|
| + int32_t requiredDigits = this->digits() + other.digits();
|
| + if (requiredDigits > fContext.digits) {
|
| + reduce(); // Remove any trailing zeros
|
| + int32_t requiredDigits = this->digits() + other.digits();
|
| + ensureCapacity(requiredDigits, status);
|
| + }
|
| + uprv_decNumberMultiply(fDecNumber, fDecNumber, other.fDecNumber, &fContext);
|
| + fHaveDouble = FALSE;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +
|
| +/*
|
| + * Divide
|
| + * The number will _not_ be expanded for inexact results.
|
| + * TODO: probably should expand some, for rounding increments that
|
| + * could add a few digits, e.g. .25, but not expand arbitrarily.
|
| + */
|
| +void
|
| +DigitList::div(const DigitList &other, UErrorCode &status) {
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + uprv_decNumberDivide(fDecNumber, fDecNumber, other.fDecNumber, &fContext);
|
| + fHaveDouble = FALSE;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +
|
| +/*
|
| + * ensureCapacity. Grow the digit storage for the number if it's less than the requested
|
| + * amount. Never reduce it. Available size is kept in fContext.digits.
|
| + */
|
| +void
|
| +DigitList::ensureCapacity(int32_t requestedCapacity, UErrorCode &status) {
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + if (requestedCapacity <= 0) {
|
| + status = U_ILLEGAL_ARGUMENT_ERROR;
|
| + return;
|
| + }
|
| + if (requestedCapacity > DEC_MAX_DIGITS) {
|
| + // Don't report an error for requesting too much.
|
| + // Arithemetic Results will be rounded to what can be supported.
|
| + // At 999,999,999 max digits, exceeding the limit is not too likely!
|
| + requestedCapacity = DEC_MAX_DIGITS;
|
| + }
|
| + if (requestedCapacity > fContext.digits) {
|
| + decNumber *newBuffer = fStorage.resize(requestedCapacity, fStorage.getCapacity());
|
| + if (newBuffer == NULL) {
|
| + status = U_MEMORY_ALLOCATION_ERROR;
|
| + return;
|
| + }
|
| + fContext.digits = requestedCapacity;
|
| + fDecNumber = newBuffer;
|
| + }
|
| +}
|
| +
|
| +// -------------------------------------
|
| +
|
| +/**
|
| + * Round the representation to the given number of digits.
|
| + * @param maximumDigits The maximum number of digits to be shown.
|
| + * Upon return, count will be less than or equal to maximumDigits.
|
| + */
|
| +void
|
| +DigitList::round(int32_t maximumDigits)
|
| +{
|
| + int32_t savedDigits = fContext.digits;
|
| + fContext.digits = maximumDigits;
|
| + uprv_decNumberPlus(fDecNumber, fDecNumber, &fContext);
|
| + fContext.digits = savedDigits;
|
| + uprv_decNumberTrim(fDecNumber);
|
| + fHaveDouble = FALSE;
|
| +}
|
| +
|
| +
|
| +void
|
| +DigitList::roundFixedPoint(int32_t maximumFractionDigits) {
|
| + trim(); // Remove trailing zeros.
|
| + if (fDecNumber->exponent >= -maximumFractionDigits) {
|
| + return;
|
| + }
|
| + decNumber scale; // Dummy decimal number, but with the desired number of
|
| + uprv_decNumberZero(&scale); // fraction digits.
|
| + scale.exponent = -maximumFractionDigits;
|
| + scale.lsu[0] = 1;
|
| +
|
| + uprv_decNumberQuantize(fDecNumber, fDecNumber, &scale, &fContext);
|
| + trim();
|
| + fHaveDouble = FALSE;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +
|
| +void
|
| +DigitList::toIntegralValue() {
|
| + uprv_decNumberToIntegralValue(fDecNumber, fDecNumber, &fContext);
|
| +}
|
| +
|
| +
|
| +// -------------------------------------
|
| +UBool
|
| +DigitList::isZero() const
|
| +{
|
| + return decNumberIsZero(fDecNumber);
|
| +}
|
| +
|
| +
|
| +U_NAMESPACE_END
|
| +#endif // #if !UCONFIG_NO_FORMATTING
|
| +
|
| +//eof
|
|
|
| Property changes on: icu46/source/i18n/digitlst.cpp
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|