Add qFpClassify() to mirror std::fpclassify()

The rules of std don't permit us to add an overload for
fpclassify(qfloat16), so we need our own equivalent that we *can*
overload.  Deploy it in the few places we use fpclassify().

Extended qnumeric's testing to cover qFpClassify().

Change-Id: Ie5a0a5cc24599d1571404c573d33c682b0d305a5
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
This commit is contained in:
Edward Welbourne 2019-03-13 18:49:04 +01:00
parent 368eb2ecec
commit 84aea6c091
6 changed files with 84 additions and 12 deletions

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE
/*!
Returns \c true if the double \a {d} is equivalent to infinity.
\relates <QtGlobal>
\sa qInf()
*/
Q_CORE_EXPORT bool qIsInf(double d) { return qt_is_inf(d); }
@ -64,6 +65,7 @@ Q_CORE_EXPORT bool qIsFinite(double d) { return qt_is_finite(d); }
/*!
Returns \c true if the float \a {f} is equivalent to infinity.
\relates <QtGlobal>
\sa qInf()
*/
Q_CORE_EXPORT bool qIsInf(float f) { return qt_is_inf(f); }
@ -88,15 +90,36 @@ Q_CORE_EXPORT double qSNaN() { return qt_snan(); }
/*!
Returns the bit pattern of a quiet NaN as a double.
\relates <QtGlobal>
\sa qIsNaN()
*/
Q_CORE_EXPORT double qQNaN() { return qt_qnan(); }
/*!
Returns the bit pattern for an infinite number as a double.
\relates <QtGlobal>
\sa qIsInf()
*/
Q_CORE_EXPORT double qInf() { return qt_inf(); }
/*!
\relates <QtGlobal>
Classifies a floating-point value.
The return values are defined in \c{<cmath>}: returns one of the following,
determined by the floating-point class of \a val:
\list
\li FP_NAN not a number
\li FP_INFINITE infinities (positive or negative)
\li FP_NORMAL finite with a full mantissa
\li FP_SUBNORMAL finite with a reduced mantissa
\endlist
*/
Q_CORE_EXPORT int qFpClassify(double val) { return qt_fpclassify(val); }
/*!
\overload
*/
Q_CORE_EXPORT int qFpClassify(float val) { return qt_fpclassify(val); }
/*!

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -48,9 +48,11 @@ QT_BEGIN_NAMESPACE
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsInf(double d);
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsNaN(double d);
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsFinite(double d);
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION int qFpClassify(double val);
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsInf(float f);
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsNaN(float f);
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsFinite(float f);
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION int qFpClassify(float val);
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qSNaN();
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qQNaN();
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qInf();

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@ -85,9 +85,11 @@ namespace qnumeric_std_wrapper {
Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(double d) { using namespace std; return isnan(d); }
Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(double d) { using namespace std; return isinf(d); }
Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(double d) { using namespace std; return isfinite(d); }
Q_DECL_CONST_FUNCTION static inline int math_h_fpclassify(double d) { using namespace std; return fpclassify(d); }
Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(float f) { using namespace std; return isnan(f); }
Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(float f) { using namespace std; return isinf(f); }
Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(float f) { using namespace std; return isfinite(f); }
Q_DECL_CONST_FUNCTION static inline int math_h_fpclassify(float f) { using namespace std; return fpclassify(f); }
}
QT_END_NAMESPACE
// These macros from math.h conflict with the real functions in the std namespace.
@ -95,6 +97,7 @@ QT_END_NAMESPACE
# undef isnan
# undef isinf
# undef isfinite
# undef fpclassify
# endif // defined(isnan)
#endif
@ -106,16 +109,20 @@ namespace qnumeric_std_wrapper {
Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return math_h_isnan(d); }
Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return math_h_isinf(d); }
Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return math_h_isfinite(d); }
Q_DECL_CONST_FUNCTION static inline int fpclassify(double d) { return math_h_fpclassify(d); }
Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return math_h_isnan(f); }
Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return math_h_isinf(f); }
Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return math_h_isfinite(f); }
Q_DECL_CONST_FUNCTION static inline int fpclassify(float f) { return math_h_fpclassify(f); }
#else
Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return std::isnan(d); }
Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return std::isinf(d); }
Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return std::isfinite(d); }
Q_DECL_CONST_FUNCTION static inline int fpclassify(double d) { return std::fpclassify(d); }
Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return std::isnan(f); }
Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return std::isinf(f); }
Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return std::isfinite(f); }
Q_DECL_CONST_FUNCTION static inline int fpclassify(float f) { return std::fpclassify(f); }
#endif
}
@ -157,6 +164,11 @@ Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(double d)
return qnumeric_std_wrapper::isfinite(d);
}
Q_DECL_CONST_FUNCTION static inline int qt_fpclassify(double d)
{
return qnumeric_std_wrapper::fpclassify(d);
}
Q_DECL_CONST_FUNCTION static inline bool qt_is_inf(float f)
{
return qnumeric_std_wrapper::isinf(f);
@ -172,6 +184,11 @@ Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(float f)
return qnumeric_std_wrapper::isfinite(f);
}
Q_DECL_CONST_FUNCTION static inline int qt_fpclassify(float f)
{
return qnumeric_std_wrapper::fpclassify(f);
}
#ifndef Q_CLANG_QDOC
namespace {
/*!

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2018 Intel Corporation.
** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
** Contact: https://www.qt.io/licensing/
@ -4002,8 +4002,8 @@ static int numericCompare(const QVariant::Private *d1, const QVariant::Private *
return 0;
// only do fuzzy comparisons for finite, non-zero numbers
int c1 = std::fpclassify(r1);
int c2 = std::fpclassify(r2);
int c1 = qFpClassify(r1);
int c2 = qFpClassify(r2);
if ((c1 == FP_NORMAL || c1 == FP_SUBNORMAL) && (c2 == FP_NORMAL || c2 == FP_SUBNORMAL)) {
if (qFuzzyCompare(r1, r2))
return 0;

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@ -2522,12 +2522,12 @@ bool QTest::compare_helper(bool success, const char *failureMsg,
template <typename T>
static bool floatingCompare(const T &t1, const T &t2)
{
switch (std::fpclassify(t1))
switch (qFpClassify(t1))
{
case FP_INFINITE:
return (t1 < 0) == (t2 < 0) && std::fpclassify(t2) == FP_INFINITE;
return (t1 < 0) == (t2 < 0) && qFpClassify(t2) == FP_INFINITE;
case FP_NAN:
return std::fpclassify(t2) == FP_NAN;
return qFpClassify(t2) == FP_NAN;
default:
return qFuzzyCompare(t1, t2);
}
@ -2631,7 +2631,7 @@ static void massageExponent(char *text)
template <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
{ \
char *msg = new char[128]; \
switch (std::fpclassify(t)) { \
switch (qFpClassify(t)) { \
case FP_INFINITE: \
qstrncpy(msg, (t < 0 ? "-inf" : "inf"), 128); \
break; \

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@ -43,6 +43,7 @@ private slots:
void fuzzyCompare_data();
void fuzzyCompare();
void qNanInf();
void classifyfp();
void floatDistance_data();
void floatDistance();
void floatDistance_double_data();
@ -123,6 +124,7 @@ void tst_QNumeric::qNanInf()
QVERIFY(qIsNaN(-nan));
QVERIFY(!(nan == nan));
QVERIFY(qIsNaN(0.0 * nan));
QCOMPARE(qFpClassify(nan), FP_NAN);
QCOMPARE(nan, nan);
QCOMPARE(nan, -nan);
QCOMPARE(nan, qQNaN());
@ -143,6 +145,34 @@ void tst_QNumeric::qNanInf()
QVERIFY(qIsNaN(0.0 * inf));
}
void tst_QNumeric::classifyfp()
{
QCOMPARE(qFpClassify(qQNaN()), FP_NAN);
QCOMPARE(qFpClassify(qInf()), FP_INFINITE);
QCOMPARE(qFpClassify(-qInf()), FP_INFINITE);
QCOMPARE(qFpClassify(DBL_MAX * 2.0), FP_INFINITE);
QCOMPARE(qFpClassify(FLT_MAX * 2.f), FP_INFINITE);
QCOMPARE(qFpClassify(DBL_MAX * -2.0), FP_INFINITE);
QCOMPARE(qFpClassify(FLT_MAX * -2.f), FP_INFINITE);
QCOMPARE(qFpClassify(1.0), FP_NORMAL);
QCOMPARE(qFpClassify(DBL_MAX), FP_NORMAL);
QCOMPARE(qFpClassify(-DBL_MAX), FP_NORMAL);
QCOMPARE(qFpClassify(DBL_MIN), FP_NORMAL);
QCOMPARE(qFpClassify(-DBL_MIN), FP_NORMAL);
QCOMPARE(qFpClassify(DBL_MIN / 2.0), FP_SUBNORMAL);
QCOMPARE(qFpClassify(DBL_MIN / -2.0), FP_SUBNORMAL);
QCOMPARE(qFpClassify(1.f), FP_NORMAL);
QCOMPARE(qFpClassify(FLT_MAX), FP_NORMAL);
QCOMPARE(qFpClassify(-FLT_MAX), FP_NORMAL);
QCOMPARE(qFpClassify(FLT_MIN), FP_NORMAL);
QCOMPARE(qFpClassify(-FLT_MIN), FP_NORMAL);
QCOMPARE(qFpClassify(FLT_MIN / 2.f), FP_SUBNORMAL);
QCOMPARE(qFpClassify(FLT_MIN / -2.f), FP_SUBNORMAL);
}
void tst_QNumeric::floatDistance_data()
{
QTest::addColumn<float>("val1");