diff options
author | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2016-01-04 15:14:21 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2016-01-05 11:29:29 +0000 |
commit | 9d6a4e70946105e6af2ff4dde281297627881928 (patch) | |
tree | 64f35e9ae5c59bdd6a43cb58644580103faa9ba2 | |
parent | 8b2f133822a22379cbbaa33fe600c30b95dc0044 (diff) |
QLocale: Accept trailing junk in qstrtod()
qstrtod() used to accept trailing junk until libdouble-conversion was
introduced and we need this behavior in order to implement EcmaScript's
parseFloat() correctly. The QString and QByteArray methods should not
accept trailing junk, though.
Task-number: QTBUG-50131
Change-Id: Ide922da0d65b2576be2c9f47f6053057eff77994
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
-rw-r--r-- | src/corelib/tools/qlocale_tools.cpp | 18 | ||||
-rw-r--r-- | src/corelib/tools/qlocale_tools_p.h | 8 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qlocale/tst_qlocale.cpp | 60 |
3 files changed, 78 insertions, 8 deletions
diff --git a/src/corelib/tools/qlocale_tools.cpp b/src/corelib/tools/qlocale_tools.cpp index f766a301c5..44704b3575 100644 --- a/src/corelib/tools/qlocale_tools.cpp +++ b/src/corelib/tools/qlocale_tools.cpp @@ -271,7 +271,8 @@ void doubleToAscii(double d, QLocaleData::DoubleForm form, int precision, char * --length; } -double asciiToDouble(const char *num, int numLen, bool &ok, int &processed) +double asciiToDouble(const char *num, int numLen, bool &ok, int &processed, + TrailingJunkMode trailingJunkMode) { if (*num == '\0') { ok = false; @@ -308,7 +309,9 @@ double asciiToDouble(const char *num, int numLen, bool &ok, int &processed) double d = 0.0; #if !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED) - int conv_flags = double_conversion::StringToDoubleConverter::NO_FLAGS; + int conv_flags = (trailingJunkMode == TrailingJunkAllowed) ? + double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK : + double_conversion::StringToDoubleConverter::NO_FLAGS; double_conversion::StringToDoubleConverter conv(conv_flags, 0.0, qt_snan(), 0, 0); d = conv.StringToDouble(num, numLen, &processed); @@ -327,7 +330,7 @@ double asciiToDouble(const char *num, int numLen, bool &ok, int &processed) if (qDoubleSscanf(num, QT_CLOCALE, "%lf%n", &d, &processed) < 1) processed = 0; - if (processed != numLen || qIsNaN(d)) { + if ((trailingJunkMode == TrailingJunkProhibited && processed != numLen) || qIsNaN(d)) { // Implementation defined nan symbol or garbage found. We don't accept it. processed = 0; ok = false; @@ -339,7 +342,7 @@ double asciiToDouble(const char *num, int numLen, bool &ok, int &processed) // We assume that any infinity symbol has to contain a character that cannot be part of a // "normal" number (that is 0-9, ., -, +, e). ok = false; - for (int i = 0; i < numLen; ++i) { + for (int i = 0; i < processed; ++i) { char c = num[i]; if ((c < '0' || c > '9') && c != '.' && c != '-' && c != '+' && c != 'e') { // Garbage found @@ -351,11 +354,12 @@ double asciiToDouble(const char *num, int numLen, bool &ok, int &processed) } #endif // !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED) - Q_ASSERT(processed == numLen); // Otherwise we would have gotten NaN or sorted it out above. + // Otherwise we would have gotten NaN or sorted it out above. + Q_ASSERT(trailingJunkMode == TrailingJunkAllowed || processed == numLen); // Check if underflow has occurred. if (isZero(d)) { - for (int i = 0; i < numLen; ++i) { + for (int i = 0; i < processed; ++i) { if (num[i] >= '1' && num[i] <= '9') { // if a digit before any 'e' is not 0, then a non-zero number was intended. ok = false; @@ -529,7 +533,7 @@ double qstrtod(const char *s00, const char **se, bool *ok) bool nonNullOk = false; int len = static_cast<int>(strlen(s00)); Q_ASSERT(len >= 0); - double d = asciiToDouble(s00, len, nonNullOk, processed); + double d = asciiToDouble(s00, len, nonNullOk, processed, TrailingJunkAllowed); if (se) *se = s00 + processed; if (ok) diff --git a/src/corelib/tools/qlocale_tools_p.h b/src/corelib/tools/qlocale_tools_p.h index 3a8036c642..f28b3dffcd 100644 --- a/src/corelib/tools/qlocale_tools_p.h +++ b/src/corelib/tools/qlocale_tools_p.h @@ -66,7 +66,13 @@ QT_BEGIN_NAMESPACE -double asciiToDouble(const char *num, int numLen, bool &ok, int &processed); +enum TrailingJunkMode { + TrailingJunkProhibited, + TrailingJunkAllowed +}; + +double asciiToDouble(const char *num, int numLen, bool &ok, int &processed, + TrailingJunkMode trailingJunkMode = TrailingJunkProhibited); void doubleToAscii(double d, QLocaleData::DoubleForm form, int precision, char *buf, int bufSize, bool &sign, int &length, int &decpt); diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp index ee45f95d83..51f691b1a4 100644 --- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp @@ -46,6 +46,7 @@ #include <qlocale.h> #include <private/qlocale_p.h> +#include <private/qlocale_tools_p.h> #include <qnumeric.h> #if defined(Q_OS_LINUX) && !defined(__UCLIBC__) @@ -99,6 +100,8 @@ private slots: void stringToDouble(); void doubleToString_data(); void doubleToString(); + void strtod_data(); + void strtod(); void long_long_conversion_data(); void long_long_conversion(); void long_long_conversion_extra(); @@ -879,6 +882,63 @@ void tst_QLocale::doubleToString() setlocale(LC_ALL, currentLocale); } +void tst_QLocale::strtod_data() +{ + QTest::addColumn<QString>("num_str"); + QTest::addColumn<double>("num"); + QTest::addColumn<int>("processed"); + QTest::addColumn<bool>("ok"); + + QTest::newRow("3.4") << QString("3.4") << 3.4 << 3 << true; + QTest::newRow("0.035003945") << QString("0.035003945") << 0.035003945 << 11 << true; + QTest::newRow("3.5003945e-2") << QString("3.5003945e-2") << 0.035003945 << 12 << true; + QTest::newRow("0.000003945") << QString("0.000003945") << 0.000003945 << 11 << true; + QTest::newRow("3.945e-6") << QString("3.945e-6") << 0.000003945 << 8 << true; + QTest::newRow("12456789012") << QString("12456789012") << 12456789012.0 << 11 << true; + QTest::newRow("1.2456789012e10") << QString("1.2456789012e10") << 12456789012.0 << 15 << true; + + QTest::newRow("a3.4") << QString("a3.4") << 0.0 << 0 << false; + QTest::newRow("b0.035003945") << QString("b0.035003945") << 0.0 << 0 << false; + QTest::newRow("c3.5003945e-2") << QString("c3.5003945e-2") << 0.0 << 0 << false; + QTest::newRow("d0.000003945") << QString("d0.000003945") << 0.0 << 0 << false; + QTest::newRow("e3.945e-6") << QString("e3.945e-6") << 0.0 << 0 << false; + QTest::newRow("f12456789012") << QString("f12456789012") << 0.0 << 0 << false; + QTest::newRow("g1.2456789012e10") << QString("g1.2456789012e10") << 0.0 << 0 << false; + + QTest::newRow("3.4a") << QString("3.4a") << 3.4 << 3 << true; + QTest::newRow("0.035003945b") << QString("0.035003945b") << 0.035003945 << 11 << true; + QTest::newRow("3.5003945e-2c") << QString("3.5003945e-2c") << 0.035003945 << 12 << true; + QTest::newRow("0.000003945d") << QString("0.000003945d") << 0.000003945 << 11 << true; + QTest::newRow("3.945e-6e") << QString("3.945e-6e") << 0.000003945 << 8 << true; + QTest::newRow("12456789012f") << QString("12456789012f") << 12456789012.0 << 11 << true; + QTest::newRow("1.2456789012e10g") << QString("1.2456789012e10g") << 12456789012.0 << 15 << true; + + QTest::newRow("0x3.4") << QString("0x3.4") << 0.0 << 1 << true; + QTest::newRow("0x0.035003945") << QString("0x0.035003945") << 0.0 << 1 << true; + QTest::newRow("0x3.5003945e-2") << QString("0x3.5003945e-2") << 0.0 << 1 << true; + QTest::newRow("0x0.000003945") << QString("0x0.000003945") << 0.0 << 1 << true; + QTest::newRow("0x3.945e-6") << QString("0x3.945e-6") << 0.0 << 1 << true; + QTest::newRow("0x12456789012") << QString("0x12456789012") << 0.0 << 1 << true; + QTest::newRow("0x1.2456789012e10") << QString("0x1.2456789012e10") << 0.0 << 1 << true; +} + +void tst_QLocale::strtod() +{ + QFETCH(QString, num_str); + QFETCH(double, num); + QFETCH(int, processed); + QFETCH(bool, ok); + + QByteArray numData = num_str.toLatin1(); + const char *end = 0; + bool actualOk = false; + double result = qstrtod(numData.constData(), &end, &actualOk); + + QCOMPARE(result, num); + QCOMPARE(actualOk, ok); + QCOMPARE(static_cast<int>(end - numData.constData()), processed); +} + void tst_QLocale::long_long_conversion_data() { QTest::addColumn<QString>("locale_name"); |