summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2018-11-19 19:53:38 +0100
committerEdward Welbourne <edward.welbourne@qt.io>2018-11-23 10:05:24 +0000
commitce159d1a3e48308d54300560f024e8501c1395c9 (patch)
tree5893e5b235738b6abfb6352d6cdf5f1f2e9cada9
parenta9923674030980706940b3ee11145c38674bb35d (diff)
Fix toFloat()s between float and double ranges and document
Revised some toFloat()s to be consistent with the matching toDouble()s; previously, they would return infinity if toDouble() did but return 0 if toDouble() got a finite value outside float's range. That also applied to values that underflowed float's range, succeeding and returning 0 as long as they were within double's range but failing if toDouble() underflowed. Now float-underflow also fails. Amended their documentation to reflect this more consistent reality. Added some tests of out-of-range values, infinities and NaNs. [ChangeLog][QtCore][toFloat] QString, QByteArray and QLocale returned an infinity on double-overflow (since 5.7) but returned 0 on a finite double outside float's range, while setting ok to false; this was at odds with their documented behavior of returning 0 on any failure. They also succeeded, returning zero, on underflow of float's range, unless double underflowed, where they failed. Changed the handling of values outside float's range to match that of values outside double's range: fail, returning an infinity on overflow or zero on underflow. The documentation now reflects the revised behavior, which matches toDouble(). Change-Id: Ia168bcacf7def0df924840d45d8edc5f850449d6 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/corelib/tools/qbytearray.cpp3
-rw-r--r--src/corelib/tools/qlocale.cpp18
-rw-r--r--src/corelib/tools/qlocale_p.h11
-rw-r--r--src/corelib/tools/qstring.cpp6
-rw-r--r--tests/auto/corelib/tools/qlocale/tst_qlocale.cpp42
5 files changed, 60 insertions, 20 deletions
diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp
index 1b7a4366c7..2a97fa50a9 100644
--- a/src/corelib/tools/qbytearray.cpp
+++ b/src/corelib/tools/qbytearray.cpp
@@ -3899,7 +3899,8 @@ double QByteArray::toDouble(bool *ok) const
/*!
Returns the byte array converted to a \c float value.
- Returns 0.0 if the conversion fails.
+ Returns an infinity if the conversion overflows or 0.0 if the
+ conversion fails for other reasons (e.g. underflow).
If \a ok is not \c nullptr, failure is reported by setting *\a{ok}
to \c false, and success by setting *\a{ok} to \c true.
diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp
index 8f9fb3de2d..417b1e41f6 100644
--- a/src/corelib/tools/qlocale.cpp
+++ b/src/corelib/tools/qlocale.cpp
@@ -1353,8 +1353,10 @@ qulonglong QLocale::toULongLong(const QString &s, bool *ok) const
}
/*!
- Returns the float represented by the localized string \a s, or 0.0
- if the conversion failed.
+ Returns the float represented by the localized string \a s.
+
+ Returns an infinity if the conversion overflows or 0.0 if the
+ conversion fails for any other reason (e.g. underflow).
If \a ok is not \c nullptr, failure is reported by setting *\a{ok}
to \c false, and success by setting *\a{ok} to \c true.
@@ -1522,8 +1524,10 @@ qulonglong QLocale::toULongLong(const QStringRef &s, bool *ok) const
}
/*!
- Returns the float represented by the localized string \a s, or 0.0
- if the conversion failed.
+ Returns the float represented by the localized string \a s.
+
+ Returns an infinity if the conversion overflows or 0.0 if the
+ conversion fails for any other reason (e.g. underflow).
If \a ok is not \c nullptr, failure is reported by setting *\a{ok}
to \c false, and success by setting *\a{ok} to \c true.
@@ -1696,8 +1700,10 @@ qulonglong QLocale::toULongLong(QStringView s, bool *ok) const
}
/*!
- Returns the float represented by the localized string \a s, or 0.0
- if the conversion failed.
+ Returns the float represented by the localized string \a s.
+
+ Returns an infinity if the conversion overflows or 0.0 if the
+ conversion fails for any other reason (e.g. underflow).
If \a ok is not \c nullptr, failure is reported by setting *\a{ok}
to \c false, and success by setting *\a{ok} to \c true.
diff --git a/src/corelib/tools/qlocale_p.h b/src/corelib/tools/qlocale_p.h
index 6669ae7c8b..deca671627 100644
--- a/src/corelib/tools/qlocale_p.h
+++ b/src/corelib/tools/qlocale_p.h
@@ -249,7 +249,16 @@ public:
if (std::fabs(d) > std::numeric_limits<float>::max()) {
if (ok != 0)
*ok = false;
- return 0.0f;
+ const float huge = std::numeric_limits<float>::infinity();
+ return d < 0 ? -huge : huge;
+ }
+ if (std::fabs(d) >= std::numeric_limits<double>::min() // i.e. d != 0
+ && std::fabs(d) < std::numeric_limits<float>::min()) {
+ // Values smaller than std::numeric_limits<double>::min() have
+ // failed already; match them.
+ if (ok != 0)
+ *ok = false;
+ return 0;
}
return float(d);
}
diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp
index 1381e8c11c..94b1784202 100644
--- a/src/corelib/tools/qstring.cpp
+++ b/src/corelib/tools/qstring.cpp
@@ -7208,7 +7208,8 @@ double QString::toDouble(bool *ok) const
/*!
Returns the string converted to a \c float value.
- Returns 0.0 if the conversion fails.
+ Returns an infinity if the conversion overflows or 0.0 if the
+ conversion fails for other reasons (e.g. underflow).
If \a ok is not \c nullptr, failure is reported by setting *\a{ok}
to \c false, and success by setting *\a{ok} to \c true.
@@ -11821,7 +11822,8 @@ double QStringRef::toDouble(bool *ok) const
/*!
Returns the string converted to a \c float value.
- Returns 0.0 if the conversion fails.
+ Returns an infinity if the conversion overflows or 0.0 if the
+ conversion fails for other reasons (e.g. underflow).
If \a ok is not \c nullptr, failure is reported by setting *\a{ok}
to \c false, and success by setting *\a{ok} to \c true.
diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp
index 7bf6d1327e..b8f1cc568a 100644
--- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp
+++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp
@@ -877,6 +877,28 @@ void tst_QLocale::stringToDouble()
void tst_QLocale::stringToFloat_data()
{
toReal_data();
+ if (std::numeric_limits<float>::has_infinity) {
+ double huge = std::numeric_limits<float>::infinity();
+ QTest::newRow("C inf") << QString("C") << QString("inf") << true << huge;
+ QTest::newRow("C +inf") << QString("C") << QString("+inf") << true << +huge;
+ QTest::newRow("C -inf") << QString("C") << QString("-inf") << true << -huge;
+ // Overflow float, but not double:
+ QTest::newRow("C big") << QString("C") << QString("3.5e38") << false << huge;
+ QTest::newRow("C -big") << QString("C") << QString("-3.5e38") << false << -huge;
+ // Overflow double, too:
+ QTest::newRow("C huge") << QString("C") << QString("2e308") << false << huge;
+ QTest::newRow("C -huge") << QString("C") << QString("-2e308") << false << -huge;
+ }
+ if (std::numeric_limits<float>::has_quiet_NaN)
+ QTest::newRow("C qnan") << QString("C") << QString("NaN") << true << double(std::numeric_limits<float>::quiet_NaN());
+
+ // Underflow float, but not double:
+ QTest::newRow("C small") << QString("C") << QString("1e-45") << false << 0.;
+ QTest::newRow("C -small") << QString("C") << QString("-1e-45") << false << 0.;
+
+ // Underflow double, too:
+ QTest::newRow("C tiny") << QString("C") << QString("2e-324") << false << 0.;
+ QTest::newRow("C -tiny") << QString("C") << QString("-2e-324") << false << 0.;
}
void tst_QLocale::stringToFloat()
@@ -904,24 +926,24 @@ void tst_QLocale::stringToFloat()
QCOMPARE(ok, good);
}
- if (ok) {
+ if (ok || std::isinf(fnum)) {
// First use fuzzy-compare, then a more precise check:
QCOMPARE(f, fnum);
- float diff = f - fnum;
- if (diff < 0)
- diff = -diff;
- QVERIFY(diff <= MY_FLOAT_EPSILON);
+ if (std::isfinite(fnum)) {
+ float diff = f > fnum ? f - fnum : fnum - f;
+ QVERIFY(diff <= MY_FLOAT_EPSILON);
+ }
}
f = locale.toFloat(num_strRef, &ok);
QCOMPARE(ok, good);
- if (ok) {
+ if (ok || std::isinf(fnum)) {
QCOMPARE(f, fnum);
- float diff = f - fnum;
- if (diff < 0)
- diff = -diff;
- QVERIFY(diff <= MY_FLOAT_EPSILON);
+ if (std::isfinite(fnum)) {
+ float diff = f > fnum ? f - fnum : fnum - f;
+ QVERIFY(diff <= MY_FLOAT_EPSILON);
+ }
}
#undef MY_FLOAT_EPSILON
}