QLocale: Return overflowing numbers from asciiToDouble()

The behavior from before libdouble-conversion is that in case of an
overflow the OK flag is set to false, but the returned number is still
infinity, rather than 0. Also, the number of processed characters is
always set to the number of characters actually processed, unless
garbage is found.

There is an important distinction between an overflow and garbage. The
client code may accept overflows and infinity may be a valid result.
Garbage is most certainly not acceptable. Having an infinity/false result
in addition to 0/false allows the client code to distinguish those.

One application where this is useful is parsing JavaScript.

Change-Id: I4b8581568144b44fca3353c4bd9685c702762af9
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ulf Hermann 2015-11-04 12:01:08 +01:00
parent a926f675be
commit 59dbf1786f
2 changed files with 30 additions and 10 deletions

View File

@ -303,31 +303,51 @@ double asciiToDouble(const char *num, int numLen, bool &ok, int &processed)
d = conv.StringToDouble(num, numLen, &processed); d = conv.StringToDouble(num, numLen, &processed);
if (!qIsFinite(d)) { if (!qIsFinite(d)) {
processed = 0;
ok = false; ok = false;
return 0.0; if (qIsNaN(d)) {
// Garbage found. We don't accept it and return 0.
processed = 0;
return 0.0;
} else {
// Overflow. That's not OK, but we still return infinity.
return d;
}
} }
Q_ASSERT(processed == numLen); // Otherwise we would have gotten NaN
#else #else
if (qDoubleSscanf(num, QT_CLOCALE, "%lf%n", &d, &processed) < 1) if (qDoubleSscanf(num, QT_CLOCALE, "%lf%n", &d, &processed) < 1)
processed = 0; processed = 0;
if (processed != numLen || !qIsFinite(d)) { if (processed != numLen || qIsNaN(d)) {
// We stopped at a non-digit character after converting some digits // Implementation defined nan symbol or garbage found. We don't accept it.
// or we found an implementation-defined symbol for infinity or nan, which we don't accept.
processed = 0; processed = 0;
ok = false; ok = false;
return 0.0; return 0.0;
} }
if (!qIsFinite(d)) {
// Overflow. Check for implementation-defined infinity symbols and reject them.
// 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) {
char c = num[i];
if ((c < '0' || c > '9') && c != '.' && c != '-' && c != '+' && c != 'e') {
// Garbage found
processed = 0;
return 0.0;
}
}
return d;
}
#endif // !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED) #endif // !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED)
Q_ASSERT(processed == numLen); // Otherwise we would have gotten NaN or sorted it out above.
// Check if underflow has occurred. // Check if underflow has occurred.
if (isZero(d)) { if (isZero(d)) {
for (int i = 0; i < numLen; ++i) { for (int i = 0; i < numLen; ++i) {
if (num[i] >= '1' && num[i] <= '9') { if (num[i] >= '1' && num[i] <= '9') {
// if a digit before any 'e' is not 0, then a non-zero number was intended. // if a digit before any 'e' is not 0, then a non-zero number was intended.
processed = 0;
ok = false; ok = false;
return 0.0; return 0.0;
} else if (num[i] == 'e') { } else if (num[i] == 'e') {

View File

@ -1931,13 +1931,13 @@ a(QLatin1String("0.0000000000000000000000000000000000000000000000000000000000000
ok = false; ok = false;
d = a.toDouble(&ok); d = a.toDouble(&ok);
QVERIFY(!ok); // detectable overflow QVERIFY(!ok); // detectable overflow
QCOMPARE(d, 0.0); QVERIFY(qIsInf(d));
a = QLatin1String("-1e600"); a = QLatin1String("-1e600");
ok = false; ok = false;
d = a.toDouble(&ok); d = a.toDouble(&ok);
QVERIFY(!ok); // detectable underflow QVERIFY(!ok); // detectable underflow
QCOMPARE(d, 0.0); QVERIFY(qIsInf(-d));
a = QLatin1String("1e-600"); a = QLatin1String("1e-600");
ok = false; ok = false;