diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2018-11-19 19:53:38 +0100 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2018-11-23 10:05:24 +0000 |
commit | ce159d1a3e48308d54300560f024e8501c1395c9 (patch) | |
tree | 5893e5b235738b6abfb6352d6cdf5f1f2e9cada9 | |
parent | a9923674030980706940b3ee11145c38674bb35d (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.cpp | 3 | ||||
-rw-r--r-- | src/corelib/tools/qlocale.cpp | 18 | ||||
-rw-r--r-- | src/corelib/tools/qlocale_p.h | 11 | ||||
-rw-r--r-- | src/corelib/tools/qstring.cpp | 6 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qlocale/tst_qlocale.cpp | 42 |
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 } |