Refine underflow check in QLocaleData::convertDoubleToFloat()

A string can parse as a non-zero double that's smaller than the
smallest float yet be a faithful representation of the smallest float.
So rather than testing for non-zero doubles less than the smallest
float, test for non-zero doubles that cast to float zero; these
underflow.  This means small values close below the smallest float
shall round up to it, rather than down to zero, requiring a tweak to
an existing test.  Added a test for the boundary case (and tidied the
test data).

Fixes: QTBUG-74833
Change-Id: I4cb30b3c0e54683574b98253505607caaf88fbfb
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Edward Welbourne 2019-04-01 15:22:15 +02:00
parent e453222414
commit 954b73445c
2 changed files with 30 additions and 19 deletions

View File

@ -252,10 +252,8 @@ public:
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 (d != 0 && float(d) == 0) {
// Values that underflow double already failed. Match them:
if (ok != 0)
*ok = false;
return 0;

View File

@ -952,29 +952,42 @@ void tst_QLocale::stringToDouble()
void tst_QLocale::stringToFloat_data()
{
using Bounds = std::numeric_limits<float>;
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;
const QString C(QStringLiteral("C"));
if (Bounds::has_infinity) {
double huge = Bounds::infinity();
QTest::newRow("C inf") << C << QString("inf") << true << huge;
QTest::newRow("C +inf") << C << QString("+inf") << true << +huge;
QTest::newRow("C -inf") << 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;
QTest::newRow("C big") << C << QString("3.5e38") << false << huge;
QTest::newRow("C -big") << 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;
QTest::newRow("C huge") << C << QString("2e308") << false << huge;
QTest::newRow("C -huge") << 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());
if (Bounds::has_quiet_NaN)
QTest::newRow("C qnan") << C << QString("NaN") << true << double(Bounds::quiet_NaN());
// Minimal float: shouldn't underflow
QTest::newRow("C float min")
<< C << QLocale::c().toString(Bounds::denorm_min()) << true << double(Bounds::denorm_min());
QTest::newRow("C float -min")
<< C << QLocale::c().toString(-Bounds::denorm_min()) << true << -double(Bounds::denorm_min());
// 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.;
QTest::newRow("C small") << C << QString("7e-46") << false << 0.;
QTest::newRow("C -small") << C << QString("-7e-46") << false << 0.;
using Double = std::numeric_limits<double>;
QTest::newRow("C double min")
<< C << QLocale::c().toString(Double::denorm_min()) << false << 0.0;
QTest::newRow("C double -min")
<< C << QLocale::c().toString(-Double::denorm_min()) << false << 0.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.;
QTest::newRow("C tiny") << C << QString("2e-324") << false << 0.;
QTest::newRow("C -tiny") << C << QString("-2e-324") << false << 0.;
}
void tst_QLocale::stringToFloat()