Change documentation of some toDouble()s to reflect reality
They actually return infinity if conversion overflows, while still setting ok to false; they were documented to return 0 on failure, with no mention of this special handling of overflow. Documented reality rather than changing the behavior. Gave underflow as an example of failure other than overflow (toDouble()s do indeed fail on it). Added some tests of out-of-range values, infinities and NaNs. [ChangeLog][QtCore][toDouble] QString, QByteArray and QLocale return an infinity on overflow (since 5.7), while setting ok to false; this was at odds with their documented behavior of returning 0 on failure. The documentation now reflects the actual behavior. Fixes: QTBUG-71256 Change-Id: I8d7e80ba1f06091cf0f1480c341553381103703b Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
704137f8de
commit
a992367403
@ -3871,7 +3871,8 @@ ushort QByteArray::toUShort(bool *ok, int base) const
|
|||||||
/*!
|
/*!
|
||||||
Returns the byte array converted to a \c double value.
|
Returns the byte array converted to a \c double 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}
|
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.
|
to \c false, and success by setting *\a{ok} to \c true.
|
||||||
|
@ -1373,8 +1373,10 @@ float QLocale::toFloat(const QString &s, bool *ok) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the double represented by the localized string \a s, or
|
Returns the double represented by the localized string \a s.
|
||||||
0.0 if the conversion failed.
|
|
||||||
|
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}
|
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.
|
to \c false, and success by setting *\a{ok} to \c true.
|
||||||
@ -1542,8 +1544,10 @@ float QLocale::toFloat(const QStringRef &s, bool *ok) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the double represented by the localized string \a s, or
|
Returns the double represented by the localized string \a s.
|
||||||
0.0 if the conversion failed.
|
|
||||||
|
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}
|
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.
|
to \c false, and success by setting *\a{ok} to \c true.
|
||||||
@ -1711,8 +1715,10 @@ float QLocale::toFloat(QStringView s, bool *ok) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the double represented by the localized string \a s, or
|
Returns the double represented by the localized string \a s.
|
||||||
0.0 if the conversion failed.
|
|
||||||
|
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}
|
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.
|
to \c false, and success by setting *\a{ok} to \c true.
|
||||||
|
@ -7169,7 +7169,8 @@ ushort QString::toUShort(bool *ok, int base) const
|
|||||||
/*!
|
/*!
|
||||||
Returns the string converted to a \c double value.
|
Returns the string converted to a \c double 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}
|
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.
|
to \c false, and success by setting *\a{ok} to \c true.
|
||||||
@ -11794,7 +11795,8 @@ ushort QStringRef::toUShort(bool *ok, int base) const
|
|||||||
/*!
|
/*!
|
||||||
Returns the string converted to a \c double value.
|
Returns the string converted to a \c double 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}
|
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.
|
to \c false, and success by setting *\a{ok} to \c true.
|
||||||
|
@ -83,7 +83,7 @@ private slots:
|
|||||||
void matchingLocales();
|
void matchingLocales();
|
||||||
void stringToDouble_data();
|
void stringToDouble_data();
|
||||||
void stringToDouble();
|
void stringToDouble();
|
||||||
void stringToFloat_data() { stringToDouble_data(); }
|
void stringToFloat_data();
|
||||||
void stringToFloat();
|
void stringToFloat();
|
||||||
void doubleToString_data();
|
void doubleToString_data();
|
||||||
void doubleToString();
|
void doubleToString();
|
||||||
@ -155,6 +155,7 @@ private:
|
|||||||
QString m_decimal, m_thousand, m_sdate, m_ldate, m_time;
|
QString m_decimal, m_thousand, m_sdate, m_ldate, m_time;
|
||||||
QString m_sysapp;
|
QString m_sysapp;
|
||||||
bool europeanTimeZone;
|
bool europeanTimeZone;
|
||||||
|
void toReal_data();
|
||||||
|
|
||||||
class TransientLocale
|
class TransientLocale
|
||||||
{
|
{
|
||||||
@ -679,7 +680,7 @@ void tst_QLocale::unixLocaleName()
|
|||||||
#undef TEST_NAME
|
#undef TEST_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QLocale::stringToDouble_data()
|
void tst_QLocale::toReal_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("locale_name");
|
QTest::addColumn<QString>("locale_name");
|
||||||
QTest::addColumn<QString>("num_str");
|
QTest::addColumn<QString>("num_str");
|
||||||
@ -801,6 +802,32 @@ void tst_QLocale::stringToDouble_data()
|
|||||||
QTest::newRow("de_DE 9.876543,0e--2") << QString("de_DE") << QString("9.876543,0e")+QChar(8722)+QString("2") << false << 0.0;
|
QTest::newRow("de_DE 9.876543,0e--2") << QString("de_DE") << QString("9.876543,0e")+QChar(8722)+QString("2") << false << 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QLocale::stringToDouble_data()
|
||||||
|
{
|
||||||
|
toReal_data();
|
||||||
|
if (std::numeric_limits<double>::has_infinity) {
|
||||||
|
double huge = std::numeric_limits<double>::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:
|
||||||
|
QTest::newRow("C huge") << QString("C") << QString("2e308") << false << huge;
|
||||||
|
QTest::newRow("C -huge") << QString("C") << QString("-2e308") << false << -huge;
|
||||||
|
}
|
||||||
|
if (std::numeric_limits<double>::has_quiet_NaN)
|
||||||
|
QTest::newRow("C qnan") << QString("C") << QString("NaN") << true << std::numeric_limits<double>::quiet_NaN();
|
||||||
|
|
||||||
|
// In range (but outside float's range):
|
||||||
|
QTest::newRow("C big") << QString("C") << QString("3.5e38") << true << 3.5e38;
|
||||||
|
QTest::newRow("C -big") << QString("C") << QString("-3.5e38") << true << -3.5e38;
|
||||||
|
QTest::newRow("C small") << QString("C") << QString("1e-45") << true << 1e-45;
|
||||||
|
QTest::newRow("C -small") << QString("C") << QString("-1e-45") << true << -1e-45;
|
||||||
|
|
||||||
|
// Underflow:
|
||||||
|
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::stringToDouble()
|
void tst_QLocale::stringToDouble()
|
||||||
{
|
{
|
||||||
#define MY_DOUBLE_EPSILON (2.22045e-16) // 1/2^{52}; double has a 53-bit mantissa
|
#define MY_DOUBLE_EPSILON (2.22045e-16) // 1/2^{52}; double has a 53-bit mantissa
|
||||||
@ -825,28 +852,33 @@ void tst_QLocale::stringToDouble()
|
|||||||
QCOMPARE(ok, good);
|
QCOMPARE(ok, good);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok) {
|
if (ok || std::isinf(num)) {
|
||||||
// First use fuzzy-compare, then a more precise check:
|
// First use fuzzy-compare, then a more precise check:
|
||||||
QCOMPARE(d, num);
|
QCOMPARE(d, num);
|
||||||
double diff = d - num;
|
if (std::isfinite(num)) {
|
||||||
if (diff < 0)
|
double diff = d > num ? d - num : num - d;
|
||||||
diff = -diff;
|
QVERIFY(diff <= MY_DOUBLE_EPSILON);
|
||||||
QVERIFY(diff <= MY_DOUBLE_EPSILON);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d = locale.toDouble(num_strRef, &ok);
|
d = locale.toDouble(num_strRef, &ok);
|
||||||
QCOMPARE(ok, good);
|
QCOMPARE(ok, good);
|
||||||
|
|
||||||
if (ok) {
|
if (ok || std::isinf(num)) {
|
||||||
QCOMPARE(d, num);
|
QCOMPARE(d, num);
|
||||||
double diff = d - num;
|
if (std::isfinite(num)) {
|
||||||
if (diff < 0)
|
double diff = d > num ? d - num : num - d;
|
||||||
diff = -diff;
|
QVERIFY(diff <= MY_DOUBLE_EPSILON);
|
||||||
QVERIFY(diff <= MY_DOUBLE_EPSILON);
|
}
|
||||||
}
|
}
|
||||||
#undef MY_DOUBLE_EPSILON
|
#undef MY_DOUBLE_EPSILON
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QLocale::stringToFloat_data()
|
||||||
|
{
|
||||||
|
toReal_data();
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QLocale::stringToFloat()
|
void tst_QLocale::stringToFloat()
|
||||||
{
|
{
|
||||||
#define MY_FLOAT_EPSILON (2.384e-7) // 1/2^{22}; float has a 23-bit mantissa
|
#define MY_FLOAT_EPSILON (2.384e-7) // 1/2^{22}; float has a 23-bit mantissa
|
||||||
|
Loading…
x
Reference in New Issue
Block a user