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>
This commit is contained in:
parent
8b2f133822
commit
9d6a4e7094
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user