/*
**********************************************************************
-* Copyright (C) 1997-2011, International Business Machines
+* Copyright (C) 1997-2015, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
*
#include "charstr.h"
#include "cmemory.h"
#include "cstring.h"
+#include "mutex.h"
#include "putilimp.h"
#include "uassert.h"
#include <stdlib.h>
#include <stdio.h>
#include <limits>
-#if defined(U_DARWIN)
-#include <xlocale.h>
-#include <xlocale/_stdio.h>
-#endif
-
// ***************************************************************************
// class DigitList
// A wrapper onto decNumber.
*/
#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";
+//static const char LONG_MIN_REP[] = "2147483648";
+//static const char I64_MIN_REP[] = "9223372036854775808";
U_NAMESPACE_BEGIN
fDecNumber = fStorage.getAlias();
uprv_decNumberZero(fDecNumber);
- fDouble = 0.0;
- fHaveDouble = TRUE;
+ internalSetDouble(0.0);
}
// -------------------------------------
fContext.digits = fStorage.getCapacity();
uprv_decNumberCopy(fDecNumber, other.fDecNumber);
- fDouble = other.fDouble;
- fHaveDouble = other.fHaveDouble;
+ {
+ // fDouble is lazily created and cached.
+ // Avoid potential races with that happening with other.fDouble
+ // while we are doing the assignment.
+ Mutex mutex;
+
+ if(other.fHave==kDouble) {
+ fUnion.fDouble = other.fUnion.fDouble;
+ } else if(other.fHave==kInt64) {
+ fUnion.fInt64 = other.fUnion.fInt64;
+ }
+ fHave = other.fHave;
+ }
}
return *this;
}
{
uprv_decNumberZero(fDecNumber);
uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN);
- fDouble = 0.0;
- fHaveDouble = TRUE;
+ internalSetDouble(0.0);
}
// -------------------------------------
+//
+// setRoundingMode()
+// For most modes, the meaning and names are the same between the decNumber library
+// (which DigitList follows) and the ICU Formatting Rounding Mode values.
+// The flag constants are different, however.
+//
+// Note that ICU's kRoundingUnnecessary is not implemented directly by DigitList.
+// This mode, inherited from Java, means that numbers that would not format exactly
+// will return an error when formatting is attempted.
void
DigitList::setRoundingMode(DecimalFormat::ERoundingMode m) {
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;
+ case DecimalFormat::kRoundUnnecessary: r = DEC_ROUND_HALF_EVEN; break;
default:
// TODO: how to report the problem?
// Leave existing mode unchanged.
} else {
fDecNumber->bits |= DECNEG;
}
- fHaveDouble = FALSE;
+ internalClear();
}
// -------------------------------------
adjustedDigits = 0;
}
fDecNumber->exponent = d - adjustedDigits;
- fHaveDouble = FALSE;
+ internalClear();
}
int32_t
fDecNumber->lsu[0] = 0;
}
fDecNumber->digits = c;
- fHaveDouble = FALSE;
+ internalClear();
}
int32_t
U_ASSERT(v>='0' && v<='9');
v &= 0x0f;
fDecNumber->lsu[count-i-1] = v;
- fHaveDouble = FALSE;
+ internalClear();
}
char
fDecNumber->exponent--;
}
}
- fHaveDouble = FALSE;
+ internalClear();
}
// -------------------------------------
/**
- * Currently, getDouble() depends on atof() to do its conversion.
+ * Currently, getDouble() depends on strtod() to do its conversion.
*
* WARNING!!
* This is an extremely costly function. ~1/2 of the conversion time
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;
+ static char gDecimal = 0;
+ char decimalSeparator;
+ {
+ Mutex mutex;
+ if (fHave == kDouble) {
+ return fUnion.fDouble;
+ } else if(fHave == kInt64) {
+ return (double)fUnion.fInt64;
+ }
+ decimalSeparator = gDecimal;
}
- DigitList *nonConstThis = const_cast<DigitList *>(this);
- if (gDecimal == 0) {
+ if (decimalSeparator == 0) {
+ // We need to know the decimal separator character that will be used with strtod().
+ // Depends on the C runtime global locale.
+ // Most commonly is '.'
+ // TODO: caching could fail if the global locale is changed on the fly.
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];
+ decimalSeparator = rep[2];
}
+ double tDouble = 0.0;
if (isZero()) {
- nonConstThis->fDouble = 0.0;
+ tDouble = 0.0;
if (decNumberIsNegative(fDecNumber)) {
- nonConstThis->fDouble /= -1;
+ tDouble /= -1;
}
} else if (isInfinite()) {
if (std::numeric_limits<double>::has_infinity) {
- nonConstThis->fDouble = std::numeric_limits<double>::infinity();
+ tDouble = std::numeric_limits<double>::infinity();
} else {
- nonConstThis->fDouble = std::numeric_limits<double>::max();
+ tDouble = std::numeric_limits<double>::max();
}
if (!isPositive()) {
- nonConstThis->fDouble = -fDouble;
+ tDouble = -tDouble; //this was incorrectly "-fDouble" originally.
}
} else {
MaybeStackArray<char, MAX_DBL_DIGITS+18> s;
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);
+ uprv_decNumberToString(numToConvert.fDecNumber, s.getAlias());
// TODO: how many extra digits should be included for an accurate conversion?
} else {
- uprv_decNumberToString(this->fDecNumber, s);
+ uprv_decNumberToString(this->fDecNumber, s.getAlias());
}
U_ASSERT(uprv_strlen(&s[0]) < MAX_DBL_DIGITS+18);
- if (gDecimal != '.') {
- char *decimalPt = strchr(s, '.');
+ if (decimalSeparator != '.') {
+ char *decimalPt = strchr(s.getAlias(), '.');
if (decimalPt != NULL) {
- *decimalPt = gDecimal;
+ *decimalPt = decimalSeparator;
}
}
char *end = NULL;
- nonConstThis->fDouble = uprv_strtod(s, &end);
+ tDouble = uprv_strtod(s.getAlias(), &end);
+ }
+ {
+ Mutex mutex;
+ DigitList *nonConstThis = const_cast<DigitList *>(this);
+ nonConstThis->internalSetDouble(tDouble);
+ gDecimal = decimalSeparator;
}
- nonConstThis->fHaveDouble = TRUE;
- return fDouble;
+ return tDouble;
}
// -------------------------------------
* Return zero if the number cannot be represented.
*/
int64_t DigitList::getInt64() /*const*/ {
+ if(fHave==kInt64) {
+ return fUnion.fInt64;
+ }
// Truncate if non-integer.
// Return 0 if out of range.
// Range of in64_t is -9223372036854775808 to 9223372036854775807 (19 digits)
DigitList::set(int32_t source)
{
set((int64_t)source);
- fDouble = source;
- fHaveDouble = TRUE;
+ internalSetDouble(source);
}
// -------------------------------------
/**
- * @param maximumDigits The maximum digits to be generated. If zero,
- * there is no maximum -- generate all digits.
+ * Set an int64, via decnumber
*/
void
DigitList::set(int64_t source)
U_ASSERT(uprv_strlen(str) < sizeof(str));
uprv_decNumberFromString(fDecNumber, str, &fContext);
- fDouble = (double)source;
- fHaveDouble = TRUE;
+ internalSetDouble(static_cast<double>(source));
+}
+
+/**
+ * Set an int64, with no decnumber
+ */
+void
+DigitList::setInteger(int64_t source)
+{
+ fDecNumber=NULL;
+ internalSetInt64(source);
}
* be acceptable for a public API.
*/
void
-DigitList::set(const StringPiece &source, UErrorCode &status) {
+DigitList::set(const StringPiece &source, UErrorCode &status, uint32_t /*fastpathBits*/) {
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) {
+#if 0
+ if(fastpathBits==(kFastpathOk|kNoDecimal)) {
+ int32_t size = source.size();
+ const char *data = source.data();
+ int64_t r = 0;
+ int64_t m = 1;
+ // fast parse
+ while(size>0) {
+ char ch = data[--size];
+ if(ch=='+') {
+ break;
+ } else if(ch=='-') {
+ r = -r;
+ break;
+ } else {
+ int64_t d = ch-'0';
+ //printf("CH[%d]=%c, %d, *=%d\n", size,ch, (int)d, (int)m);
+ r+=(d)*m;
+ m *= 10;
+ }
+ }
+ //printf("R=%d\n", r);
+ set(r);
+ } else
+#endif
+ {
+ // 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;
+ 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) {
+ fContext.status = 0;
+ uprv_decNumberFromString(fDecNumber, source.data(), &fContext);
+ if ((fContext.status & DEC_Conversion_syntax) != 0) {
status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
+ }
}
- fHaveDouble = FALSE;
+ internalClear();
}
/**
// 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]+/
-#if defined(U_DARWIN)
- // Use NULL "C" locale_t with xlocale sprintf_l API to produce "." decimal separator
- // as expected by uprv_decNumberFromString, independently of whatever locale may be set
- // via libC API, i.e. setlocale(LC_ALL, <explicit-locale>).
- sprintf_l(rep, NULL, "%+1.*e", MAX_DBL_DIGITS - 1, source);
-#else
- sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source);
-#endif
+ // Generate a representation of the form /[+-][0-9].[0-9]+e[+-][0-9]+/
+ // Can also generate /[+-]nan/ or /[+-]inf/
+ // TODO: Use something other than sprintf() here, since it's behavior is somewhat platform specific.
+ // That is why infinity is special cased here.
+ if (uprv_isInfinite(source)) {
+ if (uprv_isNegativeInfinity(source)) {
+ uprv_strcpy(rep,"-inf"); // Handle negative infinity
+ } else {
+ uprv_strcpy(rep,"inf");
+ }
+ } else {
+ sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source);
+ }
U_ASSERT(uprv_strlen(rep) < sizeof(rep));
+ // uprv_decNumberFromString() will parse the string expecting '.' as a
+ // decimal separator, however sprintf() can use ',' in certain locales.
+ // Overwrite a ',' with '.' here before proceeding.
+ char *decimalSeparator = strchr(rep, ',');
+ if (decimalSeparator != NULL) {
+ *decimalSeparator = '.';
+ }
+
// Create a decNumber from the string.
uprv_decNumberFromString(fDecNumber, rep, &fContext);
uprv_decNumberTrim(fDecNumber);
- fDouble = source;
- fHaveDouble = TRUE;
+ internalSetDouble(source);
}
// -------------------------------------
ensureCapacity(requiredDigits, status);
}
uprv_decNumberMultiply(fDecNumber, fDecNumber, other.fDecNumber, &fContext);
- fHaveDouble = FALSE;
+ internalClear();
}
// -------------------------------------
return;
}
uprv_decNumberDivide(fDecNumber, fDecNumber, other.fDecNumber, &fContext);
- fHaveDouble = FALSE;
+ internalClear();
}
// -------------------------------------
uprv_decNumberPlus(fDecNumber, fDecNumber, &fContext);
fContext.digits = savedDigits;
uprv_decNumberTrim(fDecNumber);
- fHaveDouble = FALSE;
+ internalClear();
}
uprv_decNumberQuantize(fDecNumber, fDecNumber, &scale, &fContext);
trim();
- fHaveDouble = FALSE;
+ internalClear();
}
// -------------------------------------
return decNumberIsZero(fDecNumber);
}
-
U_NAMESPACE_END
#endif // #if !UCONFIG_NO_FORMATTING