summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qlocale.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools/qlocale.cpp')
-rw-r--r--src/corelib/tools/qlocale.cpp265
1 files changed, 114 insertions, 151 deletions
diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp
index b1f53dc7a2..1f4d0c88c5 100644
--- a/src/corelib/tools/qlocale.cpp
+++ b/src/corelib/tools/qlocale.cpp
@@ -521,7 +521,7 @@ int qt_repeatCount(const QString &s, int i)
}
static const QLocaleData *default_data = 0;
-static uint default_number_options = 0;
+static QLocale::NumberOptions default_number_options = QLocale::DefaultNumberOptions;
static const QLocaleData *const c_data = locale_data;
static QLocalePrivate *c_private()
@@ -699,7 +699,8 @@ static QLocalePrivate *localePrivateByName(const QString &name)
if (name == QLatin1String("C"))
return c_private();
const QLocaleData *data = findLocaleData(name);
- return QLocalePrivate::create(data, data->m_language_id == QLocale::C ? QLocale::OmitGroupSeparator : 0);
+ return QLocalePrivate::create(data, data->m_language_id == QLocale::C ?
+ QLocale::OmitGroupSeparator : QLocale::DefaultNumberOptions);
}
static QLocalePrivate *findLocalePrivate(QLocale::Language language, QLocale::Script script,
@@ -710,7 +711,7 @@ static QLocalePrivate *findLocalePrivate(QLocale::Language language, QLocale::Sc
const QLocaleData *data = QLocaleData::findLocaleData(language, script, country);
- int numberOptions = 0;
+ QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions;
// If not found, should default to system
if (data->m_language_id == QLocale::C && language != QLocale::C) {
@@ -903,7 +904,7 @@ void QLocale::setNumberOptions(NumberOptions options)
*/
QLocale::NumberOptions QLocale::numberOptions() const
{
- return static_cast<NumberOption>(d->m_numberOptions);
+ return static_cast<NumberOptions>(d->m_numberOptions);
}
/*!
@@ -1071,13 +1072,13 @@ QString QLocale::name() const
}
static qlonglong toIntegral_helper(const QLocaleData *d, const QChar *data, int len, bool *ok,
- QLocaleData::GroupSeparatorMode mode, qlonglong)
+ QLocale::NumberOptions mode, qlonglong)
{
return d->stringToLongLong(data, len, 10, ok, mode);
}
static qulonglong toIntegral_helper(const QLocaleData *d, const QChar *data, int len, bool *ok,
- QLocaleData::GroupSeparatorMode mode, qulonglong)
+ QLocale::NumberOptions mode, qulonglong)
{
return d->stringToUnsLongLong(data, len, 10, ok, mode);
}
@@ -1089,13 +1090,8 @@ T toIntegral_helper(const QLocalePrivate *d, const QChar *data, int len, bool *o
const bool isUnsigned = T(0) < T(-1);
typedef typename QtPrivate::QConditional<isUnsigned, qulonglong, qlonglong>::Type Int64;
- QLocaleData::GroupSeparatorMode mode
- = d->m_numberOptions & QLocale::RejectGroupSeparator
- ? QLocaleData::FailOnGroupSeparators
- : QLocaleData::ParseGroupSeparators;
-
// we select the right overload by the last, unused parameter
- Int64 val = toIntegral_helper(d->m_data, data, len, ok, mode, Int64());
+ Int64 val = toIntegral_helper(d->m_data, data, len, ok, d->m_numberOptions, Int64());
if (T(val) != val) {
if (ok)
*ok = false;
@@ -1314,12 +1310,7 @@ float QLocale::toFloat(const QString &s, bool *ok) const
double QLocale::toDouble(const QString &s, bool *ok) const
{
- QLocaleData::GroupSeparatorMode mode
- = d->m_numberOptions & RejectGroupSeparator
- ? QLocaleData::FailOnGroupSeparators
- : QLocaleData::ParseGroupSeparators;
-
- return d->m_data->stringToDouble(s.constData(), s.size(), ok, mode);
+ return d->m_data->stringToDouble(s.constData(), s.size(), ok, d->m_numberOptions);
}
/*!
@@ -1488,12 +1479,7 @@ float QLocale::toFloat(const QStringRef &s, bool *ok) const
double QLocale::toDouble(const QStringRef &s, bool *ok) const
{
- QLocaleData::GroupSeparatorMode mode
- = d->m_numberOptions & RejectGroupSeparator
- ? QLocaleData::FailOnGroupSeparators
- : QLocaleData::ParseGroupSeparators;
-
- return d->m_data->stringToDouble(s.constData(), s.size(), ok, mode);
+ return d->m_data->stringToDouble(s.constData(), s.size(), ok, d->m_numberOptions);
}
@@ -2019,6 +2005,8 @@ QString QLocale::toString(double i, char f, int prec) const
if (!(d->m_numberOptions & OmitGroupSeparator))
flags |= QLocaleData::ThousandsGroup;
+ if (!(d->m_numberOptions & OmitLeadingZeroInExponent))
+ flags |= QLocaleData::ZeroPadExponent;
return d->m_data->doubleToString(i, prec, form, -1, flags);
}
@@ -2743,57 +2731,33 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q
const QChar exponential, const QChar group, const QChar decimal,
double d, int precision, DoubleForm form, int width, unsigned flags)
{
- if (precision < 0)
+ if (precision != QLocale::FloatingPointShortest && precision < 0)
precision = 6;
if (width < 0)
width = 0;
bool negative = false;
- bool special_number = false; // nan, +/-inf
QString num_str;
- // Detect special numbers (nan, +/-inf)
- if (qt_is_inf(d)) {
- num_str = QString::fromLatin1("inf");
- special_number = true;
- negative = d < 0;
- } else if (qt_is_nan(d)) {
- num_str = QString::fromLatin1("nan");
- special_number = true;
- }
-
- // Handle normal numbers
- if (!special_number) {
- int decpt, sign;
- QString digits;
-
- int mode;
- if (form == DFDecimal)
- mode = 3;
- else
- mode = 2;
-
- /* This next bit is a bit quirky. In DFExponent form, the precision
- is the number of digits after decpt. So that would suggest using
- mode=3 for qdtoa. But qdtoa behaves strangely when mode=3 and
- precision=0. So we get around this by using mode=2 and reasoning
- that we want precision+1 significant digits, since the decimal
- point in this mode is always after the first digit. */
- int pr = precision;
- if (form == DFExponent)
- ++pr;
-
- char *rve = 0;
- char *buff = 0;
- QT_TRY {
- digits = QLatin1String(qdtoa(d, mode, pr, &decpt, &sign, &rve, &buff));
- } QT_CATCH(...) {
- if (buff != 0)
- free(buff);
- QT_RETHROW;
- }
- if (buff != 0)
- free(buff);
+ int decpt;
+ int bufSize = 1;
+ if (precision == QLocale::FloatingPointShortest)
+ bufSize += DoubleMaxSignificant;
+ else if (form == DFDecimal) // optimize for numbers between -512k and 512k
+ bufSize += ((d > (1 << 19) || d < -(1 << 19)) ? DoubleMaxDigitsBeforeDecimal : 6) +
+ precision;
+ else // Add extra digit due to different interpretations of precision. Also, "nan" has to fit.
+ bufSize += qMax(2, precision) + 1;
+
+ QVarLengthArray<char> buf(bufSize);
+ int length;
+
+ doubleToAscii(d, form, precision, buf.data(), bufSize, negative, length, decpt);
+
+ if (qstrncmp(buf.data(), "inf", 3) == 0 || qstrncmp(buf.data(), "nan", 3) == 0) {
+ num_str = QString::fromLatin1(buf.data(), length);
+ } else { // Handle normal numbers
+ QString digits = QString::fromLatin1(buf.data(), length);
if (_zero.unicode() != '0') {
ushort z = _zero.unicode() - '0';
@@ -2806,7 +2770,7 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q
case DFExponent: {
num_str = exponentForm(_zero, decimal, exponential, group, plus, minus,
digits, decpt, precision, PMDecimalDigits,
- always_show_decpt);
+ always_show_decpt, flags & ZeroPadExponent);
break;
}
case DFDecimal: {
@@ -2819,10 +2783,23 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q
PrecisionMode mode = (flags & Alternate) ?
PMSignificantDigits : PMChopTrailingZeros;
- if (decpt != digits.length() && (decpt <= -4 || decpt > precision))
+ int cutoff = precision < 0 ? 6 : precision;
+ // Find out which representation is shorter
+ if (precision == QLocale::FloatingPointShortest && decpt > 0) {
+ cutoff = digits.length() + 4; // 'e', '+'/'-', one digit exponent
+ if (decpt <= 10) {
+ ++cutoff;
+ } else {
+ cutoff += decpt > 100 ? 2 : 1;
+ }
+ if (!always_show_decpt && digits.length() > decpt)
+ ++cutoff; // decpt shown in exponent form, but not in decimal form
+ }
+
+ if (decpt != digits.length() && (decpt <= -4 || decpt > cutoff))
num_str = exponentForm(_zero, decimal, exponential, group, plus, minus,
digits, decpt, precision, mode,
- always_show_decpt);
+ always_show_decpt, flags & ZeroPadExponent);
else
num_str = decimalForm(_zero, decimal, group,
digits, decpt, precision, mode,
@@ -2831,23 +2808,22 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q
}
}
- negative = sign != 0 && !isZero(d);
- }
-
- // pad with zeros. LeftAdjusted overrides this flag). Also, we don't
- // pad special numbers
- if (flags & QLocaleData::ZeroPadded
- && !(flags & QLocaleData::LeftAdjusted)
- && !special_number) {
- int num_pad_chars = width - num_str.length();
- // leave space for the sign
- if (negative
- || flags & QLocaleData::AlwaysShowSign
- || flags & QLocaleData::BlankBeforePositive)
- --num_pad_chars;
-
- for (int i = 0; i < num_pad_chars; ++i)
- num_str.prepend(_zero);
+ if (isZero(d))
+ negative = false;
+
+ // pad with zeros. LeftAdjusted overrides this flag). Also, we don't
+ // pad special numbers
+ if (flags & QLocaleData::ZeroPadded && !(flags & QLocaleData::LeftAdjusted)) {
+ int num_pad_chars = width - num_str.length();
+ // leave space for the sign
+ if (negative
+ || flags & QLocaleData::AlwaysShowSign
+ || flags & QLocaleData::BlankBeforePositive)
+ --num_pad_chars;
+
+ for (int i = 0; i < num_pad_chars; ++i)
+ num_str.prepend(_zero);
+ }
}
// add sign
@@ -3042,9 +3018,8 @@ QString QLocaleData::unsLongLongToString(const QChar zero, const QChar group,
number. We can't detect junk here, since we don't even know the base
of the number.
*/
-bool QLocaleData::numberToCLocale(const QChar *str, int len,
- GroupSeparatorMode group_sep_mode,
- CharBuff *result) const
+bool QLocaleData::numberToCLocale(const QChar *str, int len, QLocale::NumberOptions number_options,
+ CharBuff *result) const
{
const QChar *uc = str;
int l = len;
@@ -3066,6 +3041,7 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len,
int decpt_idx = -1;
int last_separator_idx = -1;
int start_of_digits_idx = -1;
+ int exponent_idx = -1;
while (idx < l) {
const QChar in = uc[idx];
@@ -3084,7 +3060,19 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len,
else
break;
}
- if (group_sep_mode == ParseGroupSeparators) {
+
+ if (number_options & QLocale::RejectLeadingZeroInExponent) {
+ if (out == 'e' || out == 'E') {
+ exponent_idx = idx;
+ } else if (exponent_idx != -1) {
+ if (out >= '1' && out <= '9')
+ exponent_idx = -1; // leading digit is not 0, forget exponent_idx
+ else if (out == '0' && idx < l - 1)
+ return false;
+ }
+ }
+
+ if (!(number_options & QLocale::RejectGroupSeparator)) {
if (start_of_digits_idx == -1 && out >= '0' && out <= '9') {
start_of_digits_idx = idx;
} else if (out == ',') {
@@ -3127,7 +3115,7 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len,
++idx;
}
- if (group_sep_mode == ParseGroupSeparators) {
+ if (!(number_options & QLocale::RejectGroupSeparator)) {
// group separator post-processing
// did we end in a separator?
if (last_separator_idx + 1 == idx)
@@ -3142,7 +3130,7 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len,
}
bool QLocaleData::validateChars(const QString &str, NumberMode numMode, QByteArray *buff,
- int decDigits, bool rejectGroupSeparators) const
+ int decDigits, QLocale::NumberOptions number_options) const
{
buff->clear();
buff->reserve(str.length());
@@ -3164,6 +3152,13 @@ bool QLocaleData::validateChars(const QString &str, NumberMode numMode, QByteArr
if (dec && decDigits != -1 && decDigits < ++decDigitCnt)
return false;
}
+
+ // The only non-digit character after the 'e' can be '+' or '-'.
+ // If a zero is directly after that, then the exponent is zero-padded.
+ if ((number_options & QLocale::RejectLeadingZeroInExponent) && c == '0' && eCnt > 0 &&
+ !lastWasDigit)
+ return false;
+
lastWasDigit = true;
} else {
switch (c) {
@@ -3203,7 +3198,8 @@ bool QLocaleData::validateChars(const QString &str, NumberMode numMode, QByteArr
case ',':
//it can only be placed after a digit which is before the decimal point
- if (rejectGroupSeparators || !lastWasDigit || decPointCnt > 0)
+ if ((number_options & QLocale::RejectGroupSeparator) || !lastWasDigit ||
+ decPointCnt > 0)
return false;
break;
@@ -3235,22 +3231,27 @@ bool QLocaleData::validateChars(const QString &str, NumberMode numMode, QByteArr
}
double QLocaleData::stringToDouble(const QChar *begin, int len, bool *ok,
- GroupSeparatorMode group_sep_mode) const
+ QLocale::NumberOptions number_options) const
{
CharBuff buff;
- if (!numberToCLocale(begin, len, group_sep_mode, &buff)) {
+ if (!numberToCLocale(begin, len, number_options, &buff)) {
if (ok != 0)
*ok = false;
return 0.0;
}
- return bytearrayToDouble(buff.constData(), ok);
+ int processed = 0;
+ bool nonNullOk = false;
+ double d = asciiToDouble(buff.constData(), buff.length() - 1, nonNullOk, processed);
+ if (ok)
+ *ok = nonNullOk;
+ return d;
}
-qlonglong QLocaleData::stringToLongLong(const QChar *begin, int len, int base,
- bool *ok, GroupSeparatorMode group_sep_mode) const
+qlonglong QLocaleData::stringToLongLong(const QChar *begin, int len, int base, bool *ok,
+ QLocale::NumberOptions number_options) const
{
CharBuff buff;
- if (!numberToCLocale(begin, len, group_sep_mode, &buff)) {
+ if (!numberToCLocale(begin, len, number_options, &buff)) {
if (ok != 0)
*ok = false;
return 0;
@@ -3259,11 +3260,11 @@ qlonglong QLocaleData::stringToLongLong(const QChar *begin, int len, int base,
return bytearrayToLongLong(buff.constData(), base, ok);
}
-qulonglong QLocaleData::stringToUnsLongLong(const QChar *begin, int len, int base,
- bool *ok, GroupSeparatorMode group_sep_mode) const
+qulonglong QLocaleData::stringToUnsLongLong(const QChar *begin, int len, int base, bool *ok,
+ QLocale::NumberOptions number_options) const
{
CharBuff buff;
- if (!numberToCLocale(begin, len, group_sep_mode, &buff)) {
+ if (!numberToCLocale(begin, len, number_options, &buff)) {
if (ok != 0)
*ok = false;
return 0;
@@ -3274,53 +3275,15 @@ qulonglong QLocaleData::stringToUnsLongLong(const QChar *begin, int len, int bas
double QLocaleData::bytearrayToDouble(const char *num, bool *ok, bool *overflow)
{
- if (ok != 0)
- *ok = true;
- if (overflow != 0)
- *overflow = false;
-
- if (*num == '\0') {
- if (ok != 0)
- *ok = false;
- return 0.0;
- }
-
- if (qstrcmp(num, "nan") == 0)
- return qt_snan();
-
- if (qstrcmp(num, "+inf") == 0 || qstrcmp(num, "inf") == 0)
- return qt_inf();
-
- if (qstrcmp(num, "-inf") == 0)
- return -qt_inf();
-
- bool _ok;
- const char *endptr;
- double d = qstrtod(num, &endptr, &_ok);
-
- if (!_ok) {
- // the only way strtod can fail with *endptr != '\0' on a non-empty
- // input string is overflow
- if (ok != 0)
- *ok = false;
- if (overflow != 0)
- *overflow = *endptr != '\0';
- return 0.0;
- }
-
- if (*endptr != '\0') {
- // we stopped at a non-digit character after converting some digits
- if (ok != 0)
- *ok = false;
- if (overflow != 0)
- *overflow = false;
- return 0.0;
- }
-
- if (ok != 0)
- *ok = true;
- if (overflow != 0)
- *overflow = false;
+ bool nonNullOk = false;
+ int len = static_cast<int>(strlen(num));
+ Q_ASSERT(len >= 0);
+ int processed = 0;
+ double d = asciiToDouble(num, len, nonNullOk, processed);
+ if (ok)
+ *ok = nonNullOk;
+ if (overflow)
+ *overflow = processed < len;
return d;
}